Merge "Add extra in DevicePolicyManager for provisioning trigger."
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 7e038ce8..2ae7d6c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,6 +92,7 @@
"core/java/android/app/IWallpaperManagerCallback.aidl",
"core/java/android/app/admin/IDeviceAdminService.aidl",
"core/java/android/app/admin/IDevicePolicyManager.aidl",
+ "core/java/android/app/admin/StartInstallingUpdateCallback.aidl",
"core/java/android/app/trust/IStrongAuthTracker.aidl",
"core/java/android/app/trust/ITrustManager.aidl",
"core/java/android/app/trust/ITrustListener.aidl",
@@ -157,10 +158,11 @@
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
"core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricService.aidl",
"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",
@@ -266,6 +268,8 @@
"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",
@@ -288,13 +292,13 @@
"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",
"core/java/android/service/notification/IConditionProvider.aidl",
"core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+ "core/java/android/service/sms/IFinancialSmsService.aidl",
"core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
"core/java/android/service/vr/IVrListener.aidl",
"core/java/android/service/vr/IVrManager.aidl",
@@ -347,8 +351,9 @@
"core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
"core/java/android/view/autofill/IAutoFillManager.aidl",
"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",
@@ -570,6 +575,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",
@@ -650,6 +656,8 @@
"core/java/com/android/server/DropboxLogTags.logtags",
"core/java/org/chromium/arc/EventLogTags.logtags",
+ ":platform-properties",
+
":framework-statslog-gen",
],
@@ -723,12 +731,16 @@
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.3-java",
+ "android.hardware.radio-V1.4-java",
"android.hardware.usb.gadget-V1.0-java",
"netd_aidl_interface-java",
+ "devicepolicyprotosnano",
],
- // Loaded with System.loadLibrary by android.view.textclassifier
required: [
+ // TODO: remove gps_debug when the build system propagates "required" properly.
+ "gps_debug.conf",
+ // Loaded with System.loadLibrary by android.view.textclassifier
"libmedia2_jni",
],
@@ -776,9 +788,11 @@
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",
+ // Needed for the ResourceId.ID_NULL constant
+ "core/java/android/content/res/ResourceId.java",
+ "core/java/android/annotation/AnyRes.java",
],
}
@@ -858,7 +872,7 @@
java_library {
name: "ext",
installable: true,
- sdk_version: "core_current",
+ no_framework_libs: true,
static_libs: [
"libphonenumber-platform",
"nist-sip",
@@ -1716,7 +1730,9 @@
"core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SdkConstant.java",
"core/java/android/annotation/StringDef.java",
+ "core/java/android/annotation/TestApi.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
+ "core/java/com/android/internal/annotations/GuardedBy.java",
],
}
diff --git a/Android.mk b/Android.mk
index 770ec20..92e33e9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -73,55 +73,35 @@
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
# ==== hiddenapi lists =======================================
-.KATI_RESTAT: \
- $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
- .KATI_IMPLICIT_OUTPUTS := \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
+.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-max-sdk-p-blacklist.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-force-blacklist.txt \
$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
$(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- --input-public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
- --input-private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
- --input-whitelists $(PRIVATE_WHITELIST_INPUTS) \
- --input-greylists \
- frameworks/base/config/hiddenapi-light-greylist.txt \
- frameworks/base/config/hiddenapi-vendor-list.txt \
- frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
- <(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \
- $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
- $(PRIVATE_GREYLIST_INPUTS) \
- --input-blacklists frameworks/base/config/hiddenapi-force-blacklist.txt \
- --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST).tmp \
- --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST).tmp \
- --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST).tmp \
- --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST).tmp
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+ --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
+ --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
+ --csv $(PRIVATE_FLAGS_INPUTS) \
+ --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 \
+ --output $@.tmp
+ $(call commit-change-for-toc,$@)
$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
frameworks/base/tools/hiddenapi/merge_csv.py \
$(PRIVATE_METADATA_INPUTS)
frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
# Include subdirectory makefiles
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6deda0c..478e4fe 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -248,6 +248,9 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media.remotedisplay.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5936ee4..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,7 +5,9 @@
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
+hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
diff --git a/api/current.txt b/api/current.txt
index e84bc8d..2ef4a39 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -150,6 +150,7 @@
field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
field public static final deprecated java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+ field public static final java.lang.String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT";
field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
@@ -159,9 +160,6 @@
field public static final java.lang.String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS";
field public static final deprecated java.lang.String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
- field public static final java.lang.String WRITE_MEDIA_AUDIO = "android.permission.WRITE_MEDIA_AUDIO";
- field public static final java.lang.String WRITE_MEDIA_IMAGES = "android.permission.WRITE_MEDIA_IMAGES";
- field public static final java.lang.String WRITE_MEDIA_VIDEO = "android.permission.WRITE_MEDIA_VIDEO";
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
@@ -483,6 +481,10 @@
field public static final int dashGap = 16843175; // 0x10101a7
field public static final int dashWidth = 16843174; // 0x10101a6
field public static final int data = 16842798; // 0x101002e
+ field public static final int dataRetentionTime = 16844189; // 0x101059d
+ field public static final int dataSentOffDevice = 16844186; // 0x101059a
+ field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b
+ field public static final int dataUsedForMonetization = 16844188; // 0x101059c
field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -1200,6 +1202,7 @@
field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
+ field public static final int selectionDividerHeight = 16844190; // 0x101059e
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int settingsSliceUri = 16844179; // 0x1010593
@@ -1315,10 +1318,10 @@
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
- field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
+ field public static final int supportsMultipleDisplays = 16844183; // 0x1010597
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
@@ -1498,7 +1501,9 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
+ field public static final int usageInfoRequired = 16844185; // 0x1010599
field public static final int use32bitAbi = 16844053; // 0x1010515
+ field public static final int useAppZygote = 16844184; // 0x1010598
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -3804,6 +3809,7 @@
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
+ method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
method public void registerForContextMenu(android.view.View);
method public boolean releaseInstance();
method public final deprecated void removeDialog(int);
@@ -3881,6 +3887,7 @@
method public deprecated void stopManagingCursor(android.database.Cursor);
method public void takeKeyEvents(boolean);
method public void triggerSearch(java.lang.String, android.os.Bundle);
+ method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
method public void unregisterForContextMenu(android.view.View);
field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1
field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0
@@ -5229,6 +5236,7 @@
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public boolean getAllowSystemGeneratedContextualActions();
method public android.app.PendingIntent getAppOverlayIntent();
method public int getBadgeIconType();
method public java.lang.String getChannelId();
@@ -5460,6 +5468,7 @@
method public android.app.Notification.Style getStyle();
method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+ method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setBadgeIconType(int);
@@ -6351,7 +6360,7 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean supportsAmbientMode();
+ method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
}
@@ -6509,6 +6518,7 @@
}
public class DevicePolicyManager {
+ method public void addCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public int addOverrideApn(android.content.ComponentName, android.telephony.data.ApnSetting);
@@ -6538,6 +6548,7 @@
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+ method public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(android.content.ComponentName);
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
@@ -6598,6 +6609,7 @@
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
+ method public void installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallUpdateCallback);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isAffiliatedUser();
@@ -6625,6 +6637,7 @@
method public int logoutUser(android.content.ComponentName);
method public void reboot(android.content.ComponentName);
method public void removeActiveAdmin(android.content.ComponentName);
+ method public boolean removeCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
method public boolean removeOverrideApn(android.content.ComponentName, int);
@@ -6840,6 +6853,16 @@
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
}
+ public static abstract class DevicePolicyManager.InstallUpdateCallback {
+ ctor public DevicePolicyManager.InstallUpdateCallback();
+ method public void onInstallUpdateError(int, java.lang.String);
+ field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
+ field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
+ field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2
+ field public static final int UPDATE_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3; // 0x3
+ }
+
public static abstract interface DevicePolicyManager.OnClearApplicationUserDataListener {
method public abstract void onApplicationUserDataCleared(java.lang.String, boolean);
}
@@ -7669,6 +7692,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);
@@ -9535,6 +9559,7 @@
method public abstract java.io.File getNoBackupFilesDir();
method public abstract java.io.File getObbDir();
method public abstract java.io.File[] getObbDirs();
+ method public abstract java.lang.String getOpPackageName();
method public abstract java.lang.String getPackageCodePath();
method public abstract android.content.pm.PackageManager getPackageManager();
method public abstract java.lang.String getPackageName();
@@ -9745,6 +9770,7 @@
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
+ method public java.lang.String getOpPackageName();
method public java.lang.String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public java.lang.String getPackageName();
@@ -10028,6 +10054,7 @@
field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
+ field public static final java.lang.String ACTION_DEFINE = "android.intent.action.DEFINE";
field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE";
field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -10118,6 +10145,7 @@
field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
+ field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
@@ -11153,8 +11181,8 @@
field public android.content.pm.ProviderInfo[] providers;
field public android.content.pm.ActivityInfo[] receivers;
field public android.content.pm.FeatureInfo[] reqFeatures;
- field public java.lang.String[] requestedPermissions;
- field public int[] requestedPermissionsFlags;
+ field public deprecated java.lang.String[] requestedPermissions;
+ field public deprecated int[] requestedPermissionsFlags;
field public android.content.pm.ServiceInfo[] services;
field public java.lang.String sharedUserId;
field public int sharedUserLabel;
@@ -11162,6 +11190,7 @@
field public android.content.pm.SigningInfo signingInfo;
field public java.lang.String[] splitNames;
field public int[] splitRevisionCodes;
+ field public android.content.pm.UsesPermissionInfo[] usesPermissions;
field public deprecated int versionCode;
field public java.lang.String versionName;
}
@@ -11312,7 +11341,7 @@
method public abstract int checkSignatures(java.lang.String, java.lang.String);
method public abstract int checkSignatures(int, int);
method public abstract void clearInstantAppCookie();
- method public abstract void clearPackagePreferredActivities(java.lang.String);
+ method public abstract deprecated void clearPackagePreferredActivities(java.lang.String);
method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
method public abstract void extendVerificationTimeout(int, int, long);
method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11356,8 +11385,8 @@
method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
- method public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
+ method public abstract deprecated int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
+ method public abstract deprecated java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
method public abstract android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11637,6 +11666,7 @@
field public java.lang.String group;
field public java.lang.CharSequence nonLocalizedDescription;
field public deprecated int protectionLevel;
+ field public boolean usageInfoRequired;
}
public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -11699,6 +11729,7 @@
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 int flags;
field public java.lang.String permission;
}
@@ -11815,6 +11846,28 @@
field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
}
+ public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDataRetention();
+ method public int getDataRetentionWeeks();
+ method public int getDataSentOffDevice();
+ method public int getDataSharedWithThirdParty();
+ method public int getDataUsedForMonetization();
+ method public int getFlags();
+ method public java.lang.String getPermission();
+ field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR;
+ field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+ field public static final int RETENTION_NOT_RETAINED = 1; // 0x1
+ field public static final int RETENTION_SPECIFIED = 4; // 0x4
+ field public static final int RETENTION_UNDEFINED = 0; // 0x0
+ field public static final int RETENTION_UNLIMITED = 3; // 0x3
+ field public static final int RETENTION_USER_SELECTED = 2; // 0x2
+ field public static final int USAGE_NO = 3; // 0x3
+ field public static final int USAGE_UNDEFINED = 0; // 0x0
+ field public static final int USAGE_USER_TRIGGERED = 2; // 0x2
+ field public static final int USAGE_YES = 1; // 0x1
+ }
+
public final class VersionedPackage implements android.os.Parcelable {
ctor public VersionedPackage(java.lang.String, int);
ctor public VersionedPackage(java.lang.String, long);
@@ -13676,6 +13729,8 @@
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);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace);
@@ -13924,6 +13979,7 @@
}
public final class Insets {
+ method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
method public static android.graphics.Insets of(int, int, int, int);
method public static android.graphics.Insets of(android.graphics.Rect);
field public static final android.graphics.Insets NONE;
@@ -14676,6 +14732,7 @@
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public long getUniqueId();
method public int getWidth();
method public boolean hasDisplayList();
method public boolean hasIdentityMatrix();
@@ -14809,6 +14866,7 @@
ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
method public android.graphics.Typeface.CustomFallbackBuilder addCustomFallback(android.graphics.fonts.FontFamily);
method public android.graphics.Typeface build();
+ method public static int getMaxCustomFallbackCount();
method public android.graphics.Typeface.CustomFallbackBuilder setStyle(android.graphics.fonts.FontStyle);
method public android.graphics.Typeface.CustomFallbackBuilder setSystemFallback(java.lang.String);
}
@@ -15028,6 +15086,7 @@
method public void invalidateSelf();
method public boolean isAutoMirrored();
method public boolean isFilterBitmap();
+ method public boolean isProjected();
method public boolean isStateful();
method public final boolean isVisible();
method public void jumpToCurrentState();
@@ -16440,6 +16499,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
@@ -17037,8 +17097,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();
@@ -17047,6 +17108,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
}
@@ -22628,7 +22691,7 @@
method public abstract void onLocationChanged(android.location.Location);
method public abstract void onProviderDisabled(java.lang.String);
method public abstract void onProviderEnabled(java.lang.String);
- method public abstract void onStatusChanged(java.lang.String, int, android.os.Bundle);
+ method public abstract deprecated void onStatusChanged(java.lang.String, int, android.os.Bundle);
}
public class LocationManager {
@@ -22640,7 +22703,7 @@
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 void clearTestProviderStatus(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);
method public java.lang.String getGnssHardwareModelName();
@@ -22677,7 +22740,7 @@
method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
method public void setTestProviderEnabled(java.lang.String, boolean);
method public void setTestProviderLocation(java.lang.String, android.location.Location);
- method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+ method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
@@ -22685,7 +22748,7 @@
field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering";
- field public static final java.lang.String KEY_STATUS_CHANGED = "status";
+ field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status";
field public static final java.lang.String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
field public static final java.lang.String NETWORK_PROVIDER = "network";
field public static final java.lang.String PASSIVE_PROVIDER = "passive";
@@ -22704,9 +22767,9 @@
method public boolean supportsAltitude();
method public boolean supportsBearing();
method public boolean supportsSpeed();
- field public static final int AVAILABLE = 2; // 0x2
- field public static final int OUT_OF_SERVICE = 0; // 0x0
- field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
+ field public static final deprecated int AVAILABLE = 2; // 0x2
+ field public static final deprecated int OUT_OF_SERVICE = 0; // 0x0
+ field public static final deprecated int TEMPORARILY_UNAVAILABLE = 1; // 0x1
}
public abstract interface OnNmeaMessageListener {
@@ -22935,7 +22998,7 @@
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
- method public static int generateAudioSessionId();
+ method public int generateAudioSessionId();
method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
@@ -23378,6 +23441,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);
@@ -23430,6 +23504,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);
}
@@ -23444,6 +23538,7 @@
}
public class ExifInterface {
+ ctor public ExifInterface(java.io.File) throws java.io.IOException;
ctor public ExifInterface(java.lang.String) throws java.io.IOException;
ctor public ExifInterface(java.io.FileDescriptor) throws java.io.IOException;
ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
@@ -23451,11 +23546,13 @@
method public java.lang.String getAttribute(java.lang.String);
method public double getAttributeDouble(java.lang.String, double);
method public int getAttributeInt(java.lang.String, int);
+ method public long[] getAttributeRange(java.lang.String);
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
method public android.graphics.Bitmap getThumbnailBitmap();
method public byte[] getThumbnailBytes();
method public long[] getThumbnailRange();
+ method public boolean hasAttribute(java.lang.String);
method public boolean hasThumbnail();
method public boolean isThumbnailCompressed();
method public void saveAttributes() throws java.io.IOException;
@@ -23627,6 +23724,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();
@@ -24490,16 +24602,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);
@@ -24575,6 +24701,7 @@
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
+ field public static final java.lang.String MIMETYPE_AUDIO_AC4 = "audio/ac4";
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";
@@ -24603,6 +24730,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 {
@@ -24993,6 +25126,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);
@@ -25735,6 +26028,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);
}
@@ -26706,6 +27019,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();
@@ -28994,20 +29308,21 @@
public class WifiManager {
method public deprecated int addNetwork(android.net.wifi.WifiConfiguration);
- method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public int addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
- method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
+ method public deprecated android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
method public deprecated boolean disableNetwork(int);
method public deprecated boolean disconnect();
method public deprecated boolean enableNetwork(int, boolean);
method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.DhcpInfo getDhcpInfo();
+ method public int getMaxNumberOfNetworkSuggestionsPerApp();
method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
@@ -29026,7 +29341,7 @@
method public deprecated boolean reassociate();
method public deprecated boolean reconnect();
method public deprecated boolean removeNetwork(int);
- method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void removePasspointConfiguration(java.lang.String);
method public deprecated boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
@@ -29055,11 +29370,18 @@
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 = 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 = 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";
- field public static final int WIFI_MODE_FULL = 1; // 0x1
+ field public static final deprecated int WIFI_MODE_FULL = 1; // 0x1
field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3
- field public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2
+ field public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; // 0x4
+ field public static final deprecated int WIFI_MODE_SCAN_ONLY = 2; // 0x2
field public static final java.lang.String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED";
field public static final int WIFI_STATE_DISABLED = 1; // 0x1
field public static final int WIFI_STATE_DISABLING = 0; // 0x0
@@ -29096,6 +29418,9 @@
method public void setReferenceCounted(boolean);
}
+ public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation {
+ }
+
public class WifiManager.WifiLock {
method public void acquire();
method public boolean isHeld();
@@ -29117,15 +29442,18 @@
method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
- method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+ method public android.net.wifi.WifiNetworkConfigBuilder setIsEnhancedOpen();
method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
- method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa2EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa2Passphrase(java.lang.String);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa3EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa3Passphrase(java.lang.String);
}
public final class WifiNetworkSuggestion implements android.os.Parcelable {
@@ -29384,11 +29712,24 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR;
+ field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1
+ field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2
+ field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0
field public java.lang.String deviceAddress;
field public int groupOwnerIntent;
field public android.net.wifi.WpsInfo wps;
}
+ public static final class WifiP2pConfig.Builder {
+ ctor public WifiP2pConfig.Builder();
+ method public android.net.wifi.p2p.WifiP2pConfig build();
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(android.net.MacAddress);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOwnerBand(int);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(java.lang.String);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(java.lang.String);
+ }
+
public class WifiP2pDevice implements android.os.Parcelable {
ctor public WifiP2pDevice();
ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice);
@@ -29455,6 +29796,7 @@
method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener);
@@ -29462,7 +29804,10 @@
method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener);
+ method public void requestDiscoveryState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener);
method public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
+ method public void requestNetworkInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener);
+ method public void requestP2pState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.P2pStateListener);
method public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener);
method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener);
@@ -29507,6 +29852,10 @@
method public abstract void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo);
}
+ public static abstract interface WifiP2pManager.DiscoveryStateListener {
+ method public abstract void onDiscoveryStateAvailable(int);
+ }
+
public static abstract interface WifiP2pManager.DnsSdServiceResponseListener {
method public abstract void onDnsSdServiceAvailable(java.lang.String, java.lang.String, android.net.wifi.p2p.WifiP2pDevice);
}
@@ -29519,6 +29868,14 @@
method public abstract void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup);
}
+ public static abstract interface WifiP2pManager.NetworkInfoListener {
+ method public abstract void onNetworkInfoAvailable(android.net.NetworkInfo);
+ }
+
+ public static abstract interface WifiP2pManager.P2pStateListener {
+ method public abstract void onP2pStateAvailable(int);
+ }
+
public static abstract interface WifiP2pManager.PeerListListener {
method public abstract void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList);
}
@@ -33435,6 +33792,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;
@@ -33944,6 +34302,7 @@
}
public final class PowerManager {
+ method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(java.lang.String);
@@ -33954,6 +34313,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";
@@ -33968,6 +34329,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 {
@@ -34220,7 +34593,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 {
@@ -34287,6 +34660,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";
@@ -34296,7 +34670,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";
@@ -36997,11 +37370,15 @@
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 java.lang.String getDocumentId(android.net.Uri);
+ 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 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.ContentResolver, 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;
@@ -37014,7 +37391,14 @@
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_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 {
@@ -37029,8 +37413,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
@@ -37061,6 +37447,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
@@ -37079,6 +37466,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);
@@ -37103,6 +37491,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);
@@ -37194,6 +37583,7 @@
method public static java.lang.String getVolumeName(android.net.Uri);
method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
method public static android.net.Uri setIncludePending(android.net.Uri);
+ method public static android.net.Uri setRequireOriginal(android.net.Uri);
field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -37291,6 +37681,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";
@@ -37370,6 +37761,17 @@
field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
}
+ public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+ field public static final java.lang.String DOWNLOAD_URI = "download_uri";
+ field public static final java.lang.String REFERER_URI = "referer_uri";
+ }
+
+ 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 android.net.Uri EXTERNAL_CONTENT_URI;
+ field public static final android.net.Uri INTERNAL_CONTENT_URI;
+ }
+
public static final class MediaStore.Files {
ctor public MediaStore.Files();
method public static android.net.Uri getContentUri(java.lang.String);
@@ -37398,8 +37800,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";
@@ -37461,7 +37863,9 @@
public static class MediaStore.PendingParams {
ctor public MediaStore.PendingParams(android.net.Uri, java.lang.String, java.lang.String);
+ method public void setDownloadUri(android.net.Uri);
method public void setPrimaryDirectory(java.lang.String);
+ method public void setRefererUri(android.net.Uri);
method public void setSecondaryDirectory(java.lang.String);
}
@@ -37521,8 +37925,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";
@@ -40841,11 +41245,9 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
- method public boolean isInAmbientMode();
method public boolean isPreview();
method public boolean isVisible();
method public void notifyColorsChanged();
- method public void onAmbientModeChanged(boolean, boolean);
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
method public android.app.WallpaperColors onComputeColors();
@@ -42785,6 +43187,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);
@@ -42871,6 +43286,7 @@
field public static final java.lang.String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+ field public static final java.lang.String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
@@ -43218,9 +43634,9 @@
public class MbmsGroupCallSession implements java.lang.AutoCloseable {
method public void close();
- method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+ method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
- method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback);
}
public class MbmsStreamingSession implements java.lang.AutoCloseable {
@@ -43605,7 +44021,7 @@
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
method public static int getSlotIndex(int);
- method public static int[] getSubscriptionIds(int);
+ method public int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
@@ -43762,6 +44178,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";
@@ -43824,6 +44241,7 @@
field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+ field public static final int NETWORK_TYPE_NR = 20; // 0x14
field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
@@ -44225,7 +44643,7 @@
public class GroupCall implements java.lang.AutoCloseable {
method public void close();
method public long getTmgi();
- method public void updateGroupCall(int[], int[]);
+ method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
@@ -44237,11 +44655,10 @@
field public static final int STATE_STOPPED = 1; // 0x1
}
- public class GroupCallCallback {
- ctor public GroupCallCallback();
- method public void onBroadcastSignalStrengthUpdated(int);
- method public void onError(int, java.lang.String);
- method public void onGroupCallStateChanged(int, int);
+ public abstract interface GroupCallCallback {
+ method public abstract void onBroadcastSignalStrengthUpdated(int);
+ method public abstract void onError(int, java.lang.String);
+ method public abstract void onGroupCallStateChanged(int, int);
field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
}
@@ -44281,6 +44698,11 @@
field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
}
+ public static class MbmsErrors.GroupCallErrors {
+ field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6
+ field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5
+ }
+
public static class MbmsErrors.InitializationErrors {
field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
@@ -44293,12 +44715,11 @@
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
- public class MbmsGroupCallSessionCallback {
- ctor public MbmsGroupCallSessionCallback();
- method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
- method public void onError(int, java.lang.String);
- method public void onMiddlewareReady();
- method public void onServiceInterfaceAvailable(java.lang.String, int);
+ public abstract interface MbmsGroupCallSessionCallback {
+ method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+ method public abstract void onError(int, java.lang.String);
+ method public abstract void onMiddlewareReady();
+ method public abstract void onServiceInterfaceAvailable(java.lang.String, int);
}
public class MbmsStreamingSessionCallback {
@@ -45599,9 +46020,14 @@
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
- public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+ public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
ctor public LineHeightSpan.Standard(int);
+ ctor public LineHeightSpan.Standard(android.os.Parcel);
method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+ method public int describeContents();
+ method public int getHeight();
+ method public int getSpanTypeId();
+ method public void writeToParcel(android.os.Parcel, int);
}
public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
@@ -45725,6 +46151,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);
@@ -47725,6 +48152,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();
@@ -48957,10 +49385,12 @@
method protected int getTopPaddingOffset();
method public android.view.TouchDelegate getTouchDelegate();
method public java.util.ArrayList<android.view.View> getTouchables();
+ method public float getTransitionAlpha();
method public java.lang.String getTransitionName();
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public long getUniqueDrawingId();
method public int getVerticalFadingEdgeLength();
method public int getVerticalScrollbarPosition();
method public int getVerticalScrollbarWidth();
@@ -49088,7 +49518,7 @@
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onProvideAutofillStructure(android.view.ViewStructure, int);
method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
- method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+ method public void onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
@@ -49165,6 +49595,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAnimationMatrix(android.graphics.Matrix);
method public void setAutofillHints(java.lang.String...);
method public void setAutofillId(android.view.autofill.AutofillId);
method public void setBackground(android.graphics.drawable.Drawable);
@@ -49216,6 +49647,7 @@
method public void setLayoutDirection(int);
method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
method public final void setLeft(int);
+ method public final void setLeftTopRightBottom(int, int, int, int);
method public void setLongClickable(boolean);
method protected final void setMeasuredDimension(int, int);
method public void setMinimumHeight(int);
@@ -49282,6 +49714,7 @@
method public void setTooltipText(java.lang.CharSequence);
method public final void setTop(int);
method public void setTouchDelegate(android.view.TouchDelegate);
+ method public void setTransitionAlpha(float);
method public final void setTransitionName(java.lang.String);
method public void setTranslationX(float);
method public void setTranslationY(float);
@@ -49304,6 +49737,8 @@
method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
+ method public void transformMatrixToGlobal(android.graphics.Matrix);
+ method public void transformMatrixToLocal(android.graphics.Matrix);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable);
method public final void updateDragShadow(android.view.View.DragShadowBuilder);
@@ -49753,6 +50188,7 @@
method public deprecated boolean isAnimationCacheEnabled();
method protected boolean isChildrenDrawingOrderEnabled();
method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
+ method public boolean isLayoutSuppressed();
method public boolean isMotionEventSplittingEnabled();
method public boolean isTransitionGroup();
method public final void layout(int, int, int, int);
@@ -49818,6 +50254,7 @@
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
method public void startViewTransition(android.view.View);
+ method public void suppressLayout(boolean);
method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22
field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000
@@ -51430,6 +51867,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 {
@@ -51831,17 +52283,82 @@
}
-package android.view.intelligence {
+package android.view.inspector {
- public final class IntelligenceManager {
- method public android.content.ComponentName getIntelligenceServiceComponentName();
- 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);
}
}
@@ -51896,6 +52413,7 @@
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;
}
public static final class ConversationActions.Message.Builder {
@@ -52164,6 +52682,7 @@
field public static final int STATUS_LINKS_APPLIED = 0; // 0x0
field public static final int STATUS_NO_LINKS_APPLIED = 2; // 0x2
field public static final int STATUS_NO_LINKS_FOUND = 1; // 0x1
+ field public static final int STATUS_UNSUPPORTED_CHARACTER = 4; // 0x4
}
public static final class TextLinks.Builder {
@@ -52732,6 +53251,7 @@
method public abstract boolean getDomStorageEnabled();
method public abstract java.lang.String getFantasyFontFamily();
method public abstract java.lang.String getFixedFontFamily();
+ method public abstract int getForceDarkMode();
method public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
method public abstract boolean getJavaScriptEnabled();
method public abstract android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
@@ -52778,6 +53298,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 abstract void setForceDarkMode(int);
method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
method public abstract void setGeolocationEnabled(boolean);
method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
@@ -52808,6 +53329,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
@@ -54157,6 +54681,7 @@
ctor public ImageView(android.content.Context, android.util.AttributeSet);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int, int);
+ method public void animateTransform(android.graphics.Matrix);
method public final void clearColorFilter();
method public boolean getAdjustViewBounds();
method public boolean getBaselineAlignBottom();
@@ -54462,6 +54987,7 @@
method public java.lang.String[] getDisplayedValues();
method public int getMaxValue();
method public int getMinValue();
+ method public int getSelectionDividerHeight();
method public int getValue();
method public boolean getWrapSelectorWheel();
method public void setDisplayedValues(java.lang.String[]);
@@ -54471,6 +54997,7 @@
method public void setOnLongPressUpdateInterval(long);
method public void setOnScrollListener(android.widget.NumberPicker.OnScrollListener);
method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener);
+ method public void setSelectionDividerHeight(int);
method public void setValue(int);
method public void setWrapSelectorWheel(boolean);
}
@@ -54823,6 +55350,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);
@@ -55454,6 +55982,8 @@
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();
method public android.text.PrecomputedText.Params getTextMetricsParams();
@@ -55477,6 +56007,7 @@
method public boolean isElegantTextHeight();
method public boolean isFallbackLineSpacing();
method public boolean isInputMethodTarget();
+ method public boolean isSingleLine();
method public boolean isSuggestionsEnabled();
method public boolean isTextSelectable();
method public int length();
@@ -55582,6 +56113,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/system-current.txt b/api/system-current.txt
index 3434b2a..57b7eed 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17,14 +17,16 @@
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";
- field public static final java.lang.String BIND_INTELLIGENCE_SERVICE = "android.permission.BIND_INTELLIGENCE_SERVICE";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
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";
@@ -56,6 +58,7 @@
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";
@@ -103,6 +106,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";
@@ -158,6 +162,7 @@
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";
field public static final java.lang.String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE";
field public static final java.lang.String RECOVERY = "android.permission.RECOVERY";
@@ -171,6 +176,7 @@
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";
@@ -214,6 +220,10 @@
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
}
+ public static final class Manifest.permission_group {
+ field public static final java.lang.String UNDEFINED = "android.permission-group.UNDEFINED";
+ }
+
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
}
@@ -222,6 +232,7 @@
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -441,10 +452,10 @@
}
public class KeyguardManager {
- method public void setPrivateNotificationsAllowed(boolean);
- method public boolean getPrivateNotificationsAllowed();
method public android.content.Intent createConfirmFactoryResetCredentialIntent(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public boolean getPrivateNotificationsAllowed();
method public void requestDismissKeyguard(android.app.Activity, java.lang.CharSequence, android.app.KeyguardManager.KeyguardDismissCallback);
+ method public void setPrivateNotificationsAllowed(boolean);
}
public class Notification implements android.os.Parcelable {
@@ -553,6 +564,10 @@
method public void onVrStateChanged(boolean);
}
+ public final class WallpaperInfo implements android.os.Parcelable {
+ method public boolean supportsAmbientMode();
+ }
+
public class WallpaperManager {
method public void clearWallpaper(int, int);
method public void setDisplayOffset(android.os.IBinder, int, int);
@@ -1060,12 +1075,12 @@
field public static final java.lang.String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
+ field public static final java.lang.String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
field public static final java.lang.String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
- field public static final java.lang.String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
@@ -1237,8 +1252,9 @@
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(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 void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
+ method public deprecated void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public void sendDeviceCustomizationReadyBroadcast();
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
@@ -1825,9 +1841,19 @@
field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubInfo> CREATOR;
}
+ public class ContextHubIntentEvent {
+ method public static android.hardware.location.ContextHubIntentEvent fromIntent(android.content.Intent);
+ method public android.hardware.location.ContextHubInfo getContextHubInfo();
+ method public int getEventType();
+ method public int getNanoAppAbortCode();
+ method public long getNanoAppId();
+ method public android.hardware.location.NanoAppMessage getNanoAppMessage();
+ }
+
public final class ContextHubManager {
method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback, java.util.concurrent.Executor);
method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback);
+ method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.app.PendingIntent, long);
method public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(android.hardware.location.ContextHubInfo, long);
method public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(android.hardware.location.ContextHubInfo, long);
method public deprecated int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
@@ -1844,6 +1870,18 @@
method public deprecated int unloadNanoApp(int);
method public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(android.hardware.location.ContextHubInfo, long);
method public deprecated int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
+ field public static final int EVENT_HUB_RESET = 6; // 0x6
+ field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
+ field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3
+ field public static final int EVENT_NANOAPP_ENABLED = 2; // 0x2
+ field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0
+ field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5
+ field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1
+ field public static final java.lang.String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO";
+ field public static final java.lang.String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
+ field public static final java.lang.String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
+ field public static final java.lang.String EXTRA_NANOAPP_ABORT_CODE = "android.hardware.location.extra.NANOAPP_ABORT_CODE";
+ field public static final java.lang.String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
}
public static abstract deprecated class ContextHubManager.Callback {
@@ -2735,7 +2773,6 @@
public class LocationManager {
method public deprecated boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
- method public android.app.PendingIntent createManageLocationPermissionIntent(java.lang.String, java.lang.String);
method public void flushGnssBatch();
method public int getGnssBatchSize();
method public java.lang.String getNetworkProviderPackage();
@@ -2844,6 +2881,7 @@
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+ field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
public static abstract class AudioManager.AudioServerStateCallback {
@@ -2896,6 +2934,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(int, byte[]);
+ }
+
}
package android.media.audiopolicy {
@@ -2983,6 +3035,7 @@
package android.media.session {
public final class MediaSessionManager {
+ method public android.media.session.ISession createSession(android.media.session.MediaSession.CallbackStub, java.lang.String, int);
method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
}
@@ -3658,8 +3711,11 @@
public class WifiManager {
method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+ method public void connect(int, android.net.wifi.WifiManager.ActionListener);
+ method public void disable(int, android.net.wifi.WifiManager.ActionListener);
+ method public void disableEphemeralNetwork(java.lang.String);
+ method public void forget(int, android.net.wifi.WifiManager.ActionListener);
method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
- method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
@@ -3669,6 +3725,7 @@
method public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
+ method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method public boolean startScan(android.os.WorkSource);
method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -3817,6 +3874,7 @@
ctor public WifiScanner.ScanSettings();
field public int band;
field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+ field public boolean ignoreLocationSettings;
field public int maxPeriodInMs;
field public int maxScansToCache;
field public int numBssidsPerScan;
@@ -3854,14 +3912,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 {
@@ -3943,6 +3993,7 @@
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";
+ field public static final java.lang.String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
field public static final java.lang.String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
@@ -4273,6 +4324,7 @@
public class UserManager {
method public void clearSeedAccountData();
+ method public android.os.UserHandle getProfileParent(android.os.UserHandle);
method public java.lang.String getSeedAccountName();
method public android.os.PersistableBundle getSeedAccountOptions();
method public java.lang.String getSeedAccountType();
@@ -4460,6 +4512,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);
@@ -4592,6 +4659,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";
@@ -4604,6 +4672,8 @@
field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used";
field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
field public static final java.lang.String LAST_SETUP_SHOWN = "last_setup_shown";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final java.lang.String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
@@ -4619,18 +4689,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 {
@@ -4809,6 +4879,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 {
@@ -4819,6 +4952,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 {
@@ -4939,46 +5124,6 @@
}
-package android.service.intelligence {
-
- public abstract class IntelligenceService extends android.app.Service {
- ctor public IntelligenceService();
- method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
- method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
- method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
- method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
- }
-
- 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 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 {
@@ -5026,14 +5171,20 @@
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+ method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
}
public final class NotificationStats implements android.os.Parcelable {
@@ -5084,6 +5235,7 @@
package android.service.oemlock {
public class OemLockManager {
+ method public java.lang.String getLockName();
method public boolean isOemUnlockAllowedByCarrier();
method public boolean isOemUnlockAllowedByUser();
method public void setOemUnlockAllowedByCarrier(boolean, byte[]);
@@ -5187,6 +5339,16 @@
}
+package android.service.sms {
+
+ public abstract class FinancialSmsService extends android.app.Service {
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.database.CursorWindow onGetSmsMessages(android.os.Bundle);
+ field public static final java.lang.String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+ }
+
+}
+
package android.service.textclassifier {
public abstract class TextClassifierService extends android.app.Service {
@@ -5244,6 +5406,15 @@
}
+package android.service.wallpaper {
+
+ public class WallpaperService.Engine {
+ method public boolean isInAmbientMode();
+ method public void onAmbientModeChanged(boolean, long);
+ }
+
+}
+
package android.telecom {
public deprecated class AudioState implements android.os.Parcelable {
@@ -5582,6 +5753,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);
@@ -5635,6 +5826,8 @@
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 setDefaultDataSubId(int);
+ method public void setDefaultSmsSubId(int);
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>);
@@ -5746,6 +5939,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);
@@ -5775,6 +5969,7 @@
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 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
@@ -5813,6 +6008,7 @@
field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200
field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000
field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000
+ field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000
field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000
field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8
field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1
@@ -6125,6 +6321,7 @@
method public android.os.Bundle getCallExtras();
method public int getCallType();
method public static int getCallTypeFromVideoState(int);
+ method public int getEmergencyServiceCategories();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
method public int getRestrictCause();
method public int getServiceType();
@@ -6137,6 +6334,7 @@
method public void setCallExtraBoolean(java.lang.String, boolean);
method public void setCallExtraInt(java.lang.String, int);
method public void setCallRestrictCause(int);
+ method public void setEmergencyServiceCategories(int);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -7026,9 +7224,9 @@
method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, int[], int[]);
+ method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
}
public class MbmsStreamingServiceBase extends android.os.Binder {
@@ -7102,7 +7300,7 @@
}
-package android.view.intelligence {
+package android.view.contentcapture {
public final class ContentCaptureEvent implements android.os.Parcelable {
method public int describeContents();
@@ -7111,26 +7309,18 @@
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
}
- public final class IntelligenceManager {
- method public java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
- method public java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
- method public void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
- method public void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
- method public void setPackageContentCaptureEnabled(java.lang.String, boolean);
- }
-
public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
method public android.view.autofill.AutofillId getParentAutofillId();
}
@@ -7212,22 +7402,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 {
@@ -7357,7 +7533,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();
@@ -7520,7 +7696,7 @@
method public default void onMovedToDisplay(int, android.content.res.Configuration);
method public abstract void onOverScrolled(int, int, boolean, boolean);
method public default void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
- method public default boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+ method public default void onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
method public abstract void onScrollChanged(int, int, int, int);
method public abstract void onSizeChanged(int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c01cf1..46cbb52 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -61,6 +61,7 @@
}
public class ActivityTaskManager {
+ method public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
method public java.lang.String listAllStacks();
method public void moveTaskToStack(int, int, boolean);
method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
@@ -81,6 +82,10 @@
field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
}
+ public class AppDetailsActivity extends android.app.Activity {
+ ctor public AppDetailsActivity();
+ }
+
public class AppOpsManager {
method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
@@ -300,16 +305,11 @@
public abstract class Context {
method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract java.lang.String getOpPackageName();
method public android.os.UserHandle getUser();
method public int getUserId();
method public void setAutofillCompatibilityEnabled(boolean);
}
- public class ContextWrapper extends android.content.Context {
- method public java.lang.String getOpPackageName();
- }
-
}
package android.content.pm {
@@ -943,6 +943,10 @@
package android.os.storage {
+ public class StorageManager {
+ method public static boolean hasIsolatedStorage();
+ }
+
public final class StorageVolume implements android.os.Parcelable {
method public java.lang.String getPath();
}
@@ -985,6 +989,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+ field public static final java.lang.String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = "autofill_smart_suggestion_emulation_flags";
field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
@@ -997,6 +1002,7 @@
}
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+ method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -1008,6 +1014,8 @@
field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final java.lang.String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
@@ -1154,13 +1162,19 @@
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+ method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
}
public abstract class NotificationListenerService extends android.app.Service {
@@ -1318,9 +1332,9 @@
method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, int[], int[]);
+ method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
}
public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index a826ec7..3723fce 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
package com.android.commands.bmgr;
import android.annotation.IntDef;
+import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
@@ -29,6 +30,7 @@
import android.app.backup.ISelectBackupTransportCallback;
import android.app.backup.RestoreSet;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Bundle;
@@ -37,8 +39,10 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,20 +53,34 @@
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-public final class Bmgr {
- IBackupManager mBmgr;
- IRestoreSession mRestore;
+/**
+ * Adb shell command for {@link android.app.backup.IBackupManager}.
+ */
+public class Bmgr {
+ public static final String TAG = "Bmgr";
- static final String BMGR_NOT_RUNNING_ERR =
+ private final IBackupManager mBmgr;
+ private IRestoreSession mRestore;
+
+ private static final String BMGR_NOT_RUNNING_ERR =
"Error: Could not access the Backup Manager. Is the system running?";
- static final String TRANSPORT_NOT_RUNNING_ERR =
+ private static final String TRANSPORT_NOT_RUNNING_ERR =
"Error: Could not access the backup transport. Is the system running?";
- static final String PM_NOT_RUNNING_ERR =
+ private static final String PM_NOT_RUNNING_ERR =
"Error: Could not access the Package Manager. Is the system running?";
private String[] mArgs;
private int mNextArg;
+ @VisibleForTesting
+ Bmgr(IBackupManager bmgr) {
+ mBmgr = bmgr;
+ }
+
+ Bmgr() {
+ mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
+
public static void main(String[] args) {
try {
new Bmgr().run(args);
@@ -78,71 +96,73 @@
return;
}
- if (!isBmgrActive()) {
+ mArgs = args;
+ mNextArg = 0;
+ int userId = parseUserId();
+ String op = nextArg();
+ Slog.v(TAG, "Running " + op + " for user:" + userId);
+
+ if (!isBmgrActive(userId)) {
return;
}
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
-
if ("enabled".equals(op)) {
- doEnabled();
+ doEnabled(userId);
return;
}
if ("enable".equals(op)) {
- doEnable();
+ doEnable(userId);
return;
}
if ("run".equals(op)) {
- doRun();
+ doRun(userId);
return;
}
if ("backup".equals(op)) {
- doBackup();
+ doBackup(userId);
return;
}
if ("init".equals(op)) {
- doInit();
+ doInit(userId);
return;
}
if ("list".equals(op)) {
- doList();
+ doList(userId);
return;
}
if ("restore".equals(op)) {
- doRestore();
+ doRestore(userId);
return;
}
if ("transport".equals(op)) {
- doTransport();
+ doTransport(userId);
return;
}
if ("wipe".equals(op)) {
- doWipe();
+ doWipe(userId);
return;
}
if ("fullbackup".equals(op)) {
- doFullTransportBackup();
+ doFullTransportBackup(userId);
return;
}
if ("backupnow".equals(op)) {
- doBackupNow();
+ doBackupNow(userId);
return;
}
if ("cancel".equals(op)) {
- doCancel();
+ doCancel(userId);
return;
}
@@ -155,15 +175,14 @@
showUsage();
}
- private boolean isBmgrActive() {
- mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+ boolean isBmgrActive(@UserIdInt int userId) {
if (mBmgr == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return false;
}
try {
- if (!mBmgr.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+ if (!mBmgr.isBackupServiceActive(userId)) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return false;
}
@@ -180,7 +199,7 @@
return enabled ? "enabled" : "disabled";
}
- private void doEnabled() {
+ private void doEnabled(@UserIdInt int userId) {
try {
boolean isEnabled = mBmgr.isBackupEnabled();
System.out.println("Backup Manager currently "
@@ -191,7 +210,7 @@
}
}
- private void doEnable() {
+ private void doEnable(@UserIdInt int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
@@ -211,7 +230,7 @@
}
}
- private void doRun() {
+ void doRun(@UserIdInt int userId) {
try {
mBmgr.backupNow();
} catch (RemoteException e) {
@@ -220,7 +239,7 @@
}
}
- private void doBackup() {
+ private void doBackup(@UserIdInt int userId) {
String pkg = nextArg();
if (pkg == null) {
showUsage();
@@ -235,7 +254,7 @@
}
}
- private void doFullTransportBackup() {
+ private void doFullTransportBackup(@UserIdInt int userId) {
System.out.println("Performing full transport backup");
String pkg;
@@ -354,8 +373,8 @@
}
}
- private void backupNowAllPackages(boolean nonIncrementalBackup, @Monitor int monitorState) {
- int userId = UserHandle.USER_SYSTEM;
+ private void backupNowAllPackages(@UserIdInt int userId, boolean nonIncrementalBackup,
+ @Monitor int monitorState) {
IPackageManager mPm =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
@@ -379,11 +398,13 @@
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
- backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState);
+ backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup,
+ monitorState);
}
}
private void backupNowPackages(
+ @UserIdInt int userId,
List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) {
int flags = 0;
if (nonIncrementalBackup) {
@@ -412,7 +433,7 @@
}
}
- private void doBackupNow() {
+ private void doBackupNow(@UserIdInt int userId) {
String pkg;
boolean backupAll = false;
boolean nonIncrementalBackup = false;
@@ -439,20 +460,20 @@
if (allPkgs.size() == 0) {
System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
"incremental backup for all packages.");
- backupNowAllPackages(nonIncrementalBackup, monitor);
+ backupNowAllPackages(userId, nonIncrementalBackup, monitor);
} else {
System.err.println("Provide only '--all' flag or list of packages.");
}
} else if (allPkgs.size() > 0) {
System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
"incremental backup for " + allPkgs.size() +" requested packages.");
- backupNowPackages(allPkgs, nonIncrementalBackup, monitor);
+ backupNowPackages(userId, allPkgs, nonIncrementalBackup, monitor);
} else {
System.err.println("Provide '--all' flag or list of packages.");
}
}
- private void doCancel() {
+ private void doCancel(@UserIdInt int userId) {
String arg = nextArg();
if ("backups".equals(arg)) {
try {
@@ -467,7 +488,7 @@
System.err.println("Unknown command.");
}
- private void doTransport() {
+ private void doTransport(@UserIdInt int userId) {
try {
String which = nextArg();
if (which == null) {
@@ -531,7 +552,7 @@
}
}
- private void doWipe() {
+ private void doWipe(@UserIdInt int userId) {
String transport = nextArg();
if (transport == null) {
showUsage();
@@ -563,7 +584,7 @@
}
}
- private void doInit() {
+ private void doInit(@UserIdInt int userId) {
ArraySet<String> transports = new ArraySet<>();
String transport;
while ((transport = nextArg()) != null) {
@@ -586,7 +607,7 @@
}
}
- private void doList() {
+ private void doList(@UserIdInt int userId) {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
doListTransports();
@@ -603,8 +624,6 @@
if ("sets".equals(arg)) {
doListRestoreSets();
- } else if ("transports".equals(arg)) {
- doListTransports();
}
mRestore.endRestoreSession();
@@ -717,7 +736,7 @@
}
}
- private void doRestore() {
+ private void doRestore(@UserIdInt int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
@@ -830,8 +849,18 @@
return arg;
}
+ private int parseUserId() {
+ String arg = nextArg();
+ if ("--user".equals(arg)) {
+ return UserHandle.parseUserArg(nextArg());
+ } else {
+ mNextArg--;
+ return UserHandle.USER_SYSTEM;
+ }
+ }
+
private static void showUsage() {
- System.err.println("usage: bmgr [backup|restore|list|transport|run]");
+ System.err.println("usage: bmgr [--user <userId>] [backup|restore|list|transport|run]");
System.err.println(" bmgr backup PACKAGE");
System.err.println(" bmgr enable BOOL");
System.err.println(" bmgr enabled");
@@ -847,6 +876,10 @@
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
System.err.println("");
+ System.err.println("The '--user' option specifies the user on which the operation is run.");
+ System.err.println("It must be the first argument before the operation.");
+ System.err.println("The default value is 0 which is the system user.");
+ System.err.println("");
System.err.println("The 'backup' command schedules a backup pass for the named package.");
System.err.println("Note that the backup pass will effectively be a no-op if the package");
System.err.println("does not actually have changed data to store.");
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 1597c8c..52a2ab4 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -77,7 +77,7 @@
+ " <BINDING> binds a typed value to a column and is formatted:\n"
+ " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
+ " <TYPE> specifies data type such as:\n"
- + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+ + " b - boolean, s - string, i - integer, l - long, f - float, d - double, n - null\n"
+ " Note: Omit the value for passing an empty string, e.g column:s:\n"
+ " Example:\n"
+ " # Add \"new_setting\" secure setting with value \"new_value\".\n"
@@ -153,6 +153,7 @@
private static final String TYPE_LONG = "l";
private static final String TYPE_FLOAT = "f";
private static final String TYPE_DOUBLE = "d";
+ private static final String TYPE_NULL = "n";
private static final String COLON = ":";
private static final String ARGUMENT_PREFIX = "--";
@@ -410,6 +411,8 @@
values.put(column, Long.parseLong(value));
} else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
values.put(column, Double.parseDouble(value));
+ } else if (TYPE_NULL.equalsIgnoreCase(type)) {
+ values.putNull(column);
} else {
throw new IllegalArgumentException("Unsupported type: " + type);
}
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/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 5a6c813..d7922bc 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -18,7 +18,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"libidmap2/BinaryStreamVisitor.cpp",
@@ -64,7 +64,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
@@ -118,7 +118,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"idmap2/Create.cpp",
@@ -165,7 +165,7 @@
],
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
":idmap2_aidl",
@@ -181,6 +181,7 @@
"libutils",
"libziparchive",
],
+ init_rc: ["idmap2d/idmap2d.rc"],
}
filegroup {
diff --git a/cmds/idmap2/idmap2d/idmap2d.rc b/cmds/idmap2/idmap2d/idmap2d.rc
new file mode 100644
index 0000000..203e7be
--- /dev/null
+++ b/cmds/idmap2/idmap2d/idmap2d.rc
@@ -0,0 +1,4 @@
+service idmap2d /system/bin/idmap2d
+ class main
+ user system
+ group system
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/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 1c3ebd8..f8fb4a6 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -24,11 +24,12 @@
#include "incidentd_util.h"
#include "section_list.h"
+#include <android/os/IncidentReportArgs.h>
#include <binder/IPCThreadState.h>
#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>
@@ -41,6 +42,13 @@
#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
+// 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 */ \
+ 3018 /* "meminfo -a --proto" */ }
+
namespace android {
namespace os {
namespace incidentd {
@@ -391,6 +399,38 @@
return NO_ERROR;
}
+status_t IncidentService::dump(int fd, const Vector<String16>& args) {
+ if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) {
+ ALOGD("Skip dumping incident. Only proto format is supported.");
+ dprintf(fd, "Incident dump only supports proto version.\n");
+ return NO_ERROR;
+ }
+
+ ALOGD("Dump incident proto");
+ IncidentReportArgs incidentArgs;
+ incidentArgs.setDest(DEST_EXPLICIT);
+ int skipped[] = SKIPPED_SECTIONS;
+ for (const Section** section = SECTION_LIST; *section; section++) {
+ const int id = (*section)->id;
+ if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+ incidentArgs.addSection(id);
+ }
+ }
+
+ if (!checkIncidentPermissions(incidentArgs).isOk()) {
+ return PERMISSION_DENIED;
+ }
+
+ int fd1 = dup(fd);
+ if (fd1 < 0) {
+ return -errno;
+ }
+
+ mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1));
+
+ return NO_ERROR;
+}
+
} // namespace incidentd
} // namespace os
} // namespace android
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index e176bfd..6252ad2 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -112,6 +112,7 @@
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) override;
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ virtual status_t dump(int fd, const Vector<String16>& args);
private:
sp<ReportRequestQueue> mQueue;
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/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
index 4948823..098d74e 100644
--- a/cmds/incidentd/src/main.cpp
+++ b/cmds/incidentd/src/main.cpp
@@ -45,7 +45,8 @@
// Create the service
sp<IncidentService> service = new IncidentService(looper);
- if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+ if (defaultServiceManager()->addService(String16("incident"), service, false,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
ALOGE("Failed to add service");
return -1;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3ee0a06..392d40a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -34,6 +34,8 @@
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
/**
* The master atom class. This message defines all of the available
@@ -156,6 +158,7 @@
ServiceLaunchReported service_launch_reported = 100;
PhenotypeFlagStateChanged phenotype_flag_state_changed = 101;
BinaryPushStateChanged binary_push_state_changed = 102;
+ DevicePolicyEvent device_policy_event = 103;
}
// Pulled events will start at field 10000.
@@ -205,6 +208,7 @@
DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
BatteryLevel battery_level = 10043;
+ BuildInformation build_information = 10044;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -1334,8 +1338,9 @@
* Log bucketed battery charge cycles.
*
* Each bucket represents cycles of the battery past
- * a given charge point. For example, bucket 1 is the
- * lowest 1/8th of the battery, and bucket 8 is 100%.
+ * a given charge point. For example, if 10 cycle buckets are
+ * initialized, bucket 1 is the lowest 1/10th of the battery,
+ * and bucket 10 is 100%.
*
* Logged from:
* /sys/class/power_supply/bms/cycle_count, via Vendor.
@@ -1349,6 +1354,8 @@
optional int32 cycle_bucket_6 = 6;
optional int32 cycle_bucket_7 = 7;
optional int32 cycle_bucket_8 = 8;
+ optional int32 cycle_bucket_9 = 9;
+ optional int32 cycle_bucket_10 = 10;
}
/**
@@ -1494,17 +1501,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;
}
/**
@@ -2011,6 +2018,9 @@
// SWAP
optional int64 swap_in_bytes = 8;
+
+ // The elapsed real time of start of the process.
+ optional int64 process_start_time_nanos = 9;
}
/*
@@ -2421,8 +2431,8 @@
*/
message CpuTimePerUid {
optional int32 uid = 1 [(is_uid) = true];
- optional uint64 user_time_millis = 2;
- optional uint64 sys_time_millis = 3;
+ optional uint64 user_time_micros = 2;
+ optional uint64 sys_time_micros = 3;
}
/**
@@ -2536,10 +2546,7 @@
// SWAP
optional int64 swap_in_bytes = 8;
- // RSS high watermark.
- // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
- // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
- // Deprecated: use ProcessMemoryHighWaterMark atom instead.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
// Elapsed real time when the process started.
@@ -2566,9 +2573,7 @@
// RSS
optional int64 rss_in_bytes = 5;
- // RSS high watermark.
- // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status.
- // Deprecated: use ProcessMemoryHighWaterMark atom instead.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
// Elapsed real time when the process started.
@@ -3324,10 +3329,61 @@
optional string process_name = 4;
// Name of the thread taken from `/proc/$PID/task/$TID/comm`
optional string thread_name = 5;
- // What frequency the CPU was running at, in KHz
- optional int32 frequency_khz = 6;
- // Time spent in frequency in milliseconds, since thread start.
- optional int32 time_millis = 7;
+
+ // Report eight different frequencies, and how much time is spent in each frequency. Frequencies
+ // are given in KHz, and time is given in milliseconds since the thread started. All eight
+ // frequencies are given here as the alternative is sending eight separate atoms. This method
+ // significantly reduces the amount of data created
+ optional int32 frequency1_khz = 6;
+ optional int32 time1_millis = 7;
+ optional int32 frequency2_khz = 8;
+ optional int32 time2_millis = 9;
+ optional int32 frequency3_khz = 10;
+ optional int32 time3_millis = 11;
+ optional int32 frequency4_khz = 12;
+ optional int32 time4_millis = 13;
+ optional int32 frequency5_khz = 14;
+ optional int32 time5_millis = 15;
+ optional int32 frequency6_khz = 16;
+ optional int32 time6_millis = 17;
+ optional int32 frequency7_khz = 18;
+ optional int32 time7_millis = 19;
+ optional int32 frequency8_khz = 20;
+ optional int32 time8_millis = 21;
+}
+
+/**
+ * Pulls information about the device's build.
+ */
+message BuildInformation {
+ // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse.
+ // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags.
+ optional string fingerprint = 1;
+
+ // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated.
+ optional string brand = 2;
+
+ // Build.PRODUCT. The name of the overall product.
+ optional string product = 3;
+
+ // Build.DEVICE. The name of the industrial design.
+ optional string device = 4;
+
+ // Build.VERSION.RELEASE. The user-visible version string. E.g., "1.0" or "3.4b5" or "bananas".
+ optional string version_release = 5;
+
+ // Build.ID. E.g. a label like "M4-rc20".
+ optional string id = 6;
+
+ // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to
+ // represent this build.
+ optional string version_incremental = 7;
+
+ // Build.TYPE. The type of build, like "user" or "eng".
+ optional string type = 8;
+
+ // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug".
+ optional string tags = 9;
}
/**
@@ -3389,3 +3445,25 @@
// (i.e. roughly since device was last significantly charged).
optional float power_milli_amp_hours = 2;
}
+
+/**
+ * Logs device policy features.
+ *
+ * Logged from:
+ * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ * packages/apps/ManagedProvisioning/src/com/android/managedprovisioning/
+ */
+message DevicePolicyEvent {
+ // The event id - unique for each event.
+ optional android.stats.devicepolicy.EventId event_id = 1;
+ // The admin package name.
+ optional string admin_package_name = 2;
+ // A generic integer parameter.
+ optional int32 integer_value = 3;
+ // A generic boolean parameter.
+ optional bool boolean_value = 4;
+ // A parameter specifying a time period in milliseconds.
+ optional uint64 time_period_millis = 5;
+ // 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];
+}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 3eb05a9..4e4b8f3 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -59,7 +59,7 @@
}
data->clear();
for (const StatsLogEventWrapper& it : returned_value) {
- data->push_back(make_shared<LogEvent>(it));
+ LogEvent::createLogEvents(it, *data);
}
VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
return true;
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index e3f251a..f501574 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -32,9 +32,10 @@
sp<UidMap> StatsPuller::mUidMap = nullptr;
void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
-// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
+ // Pullers can cause significant impact to system health and battery.
+ // So that we don't pull too frequently.
mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
}
@@ -64,8 +65,8 @@
data->setLogdWallClockTimestampNs(wallClockTimeNs);
}
if (ret && mCachedData.size() > 0) {
- mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
- (*data) = mCachedData;
+ mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ (*data) = mCachedData;
}
StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
return ret;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ab635a0..c070ca3 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -54,52 +54,42 @@
// wifi_bytes_transfer
{android::util::WIFI_BYTES_TRANSFER,
{{2, 3, 4, 5},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
// wifi_bytes_transfer_by_fg_bg
{android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
{{3, 4, 5, 6},
- {2},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
// mobile_bytes_transfer
{android::util::MOBILE_BYTES_TRANSFER,
{{2, 3, 4, 5},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
// mobile_bytes_transfer_by_fg_bg
{android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
{{3, 4, 5, 6},
- {2},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
// bluetooth_bytes_transfer
{android::util::BLUETOOTH_BYTES_TRANSFER,
{{2, 3},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
// kernel_wakelock
{android::util::KERNEL_WAKELOCK,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
// subsystem_sleep_state
{android::util::SUBSYSTEM_SLEEP_STATE,
- {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+ {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
// on_device_power_measurement
- {android::util::ON_DEVICE_POWER_MEASUREMENT,
- {{}, {}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+ {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
// cpu_time_per_freq
{android::util::CPU_TIME_PER_FREQ,
- {{3},
- {2},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
// cpu_time_per_uid
{android::util::CPU_TIME_PER_UID,
{{2, 3},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
// cpu_time_per_uid_freq
@@ -107,164 +97,140 @@
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_TIME_PER_UID_FREQ,
{{4},
- {2, 3},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
// cpu_active_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_ACTIVE_TIME,
- {{2},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+ {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
// cpu_cluster_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_CLUSTER_TIME,
- {{3},
- {2},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+ {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
// wifi_activity_energy_info
{android::util::WIFI_ACTIVITY_INFO,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
// modem_activity_info
{android::util::MODEM_ACTIVITY_INFO,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
// bluetooth_activity_info
{android::util::BLUETOOTH_ACTIVITY_INFO,
{{},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
{{},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
// system_uptime
{android::util::SYSTEM_UPTIME,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
// remaining_battery_capacity
{android::util::REMAINING_BATTERY_CAPACITY,
{{},
- {},
1 * NS_PER_SEC,
new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
// full_battery_capacity
{android::util::FULL_BATTERY_CAPACITY,
{{},
- {},
1 * NS_PER_SEC,
new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// battery_voltage
{android::util::BATTERY_VOLTAGE,
- {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
- // battery_voltage
+ {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+ // battery_level
{android::util::BATTERY_LEVEL,
- {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+ {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
{{4, 5, 6, 7, 8, 9},
- {2, 3, 10},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// native_process_memory_state
{android::util::NATIVE_PROCESS_MEMORY_STATE,
{{3, 4, 5, 6},
- {2, 7},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
{android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
{{3},
- {2},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
// temperature
- {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+ {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
// binder_calls
{android::util::BINDER_CALLS,
{{4, 5, 6, 8, 12},
- {2, 3, 7, 9, 10, 11, 13},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
// binder_calls_exceptions
{android::util::BINDER_CALLS_EXCEPTIONS,
{{},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
// looper_stats
{android::util::LOOPER_STATS,
{{5, 6, 7, 8, 9},
- {2, 3, 4, 10},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
// Disk Stats
{android::util::DISK_STATS,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
// Directory usage
{android::util::DIRECTORY_USAGE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
// Size of app's code, data, and cache
{android::util::APP_SIZE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
// Size of specific categories of files. Eg. Music.
{android::util::CATEGORY_SIZE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
// Number of fingerprints registered to each user.
{android::util::NUM_FINGERPRINTS,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
// ProcStats.
{android::util::PROC_STATS,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
// ProcStatsPkgProc.
{android::util::PROC_STATS_PKG_PROC,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
// Disk I/O stats per uid.
{android::util::DISK_IO,
- {{2,3,4,5,6,7,8,9,10,11},
- {},
+ {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
3 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DISK_IO)}},
// PowerProfile constants for power model calculations.
{android::util::POWER_PROFILE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
// Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
{android::util::PROCESS_CPU_TIME,
- {{} /* additive fields */, {} /* non additive fields */,
- 5 * NS_PER_SEC /* min cool-down in seconds*/,
- new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+ {{} /* additive fields */,
+ 5 * NS_PER_SEC /* min cool-down in seconds*/,
+ new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
{android::util::CPU_TIME_PER_THREAD_FREQ,
- {{7},
- {2, 3, 4, 5, 6},
+ {{7, 9, 11, 13, 15, 17, 19, 21},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
// DeviceCalculatedPowerUse.
{android::util::DEVICE_CALCULATED_POWER_USE,
- {{}, {}, 1 * NS_PER_SEC,
+ {{},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
// DeviceCalculatedPowerBlameUid.
{android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
- {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+ {{}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
// DeviceCalculatedPowerBlameOther.
{android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
- {{}, {},
+ {{},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+ // BuildInformation.
+ {android::util::BUILD_INFORMATION,
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index bbf5d9d..3350736 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -36,9 +36,6 @@
// The field numbers of the fields that need to be summed when merging
// isolated uid with host uid.
std::vector<int> additiveFields;
- // The field numbers of the fields that can't be merged when merging
- // data belong to isolated uid and host uid.
- std::vector<int> nonAdditiveFields;
// How long should the puller wait before doing an actual pull again. Default
// 1 sec. Set this to 0 if this is handled elsewhere.
int64_t coolDownNs = 1 * NS_PER_SEC;
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ea7fa97..0b9b6ab 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -25,67 +25,13 @@
namespace os {
namespace statsd {
+using std::list;
using std::map;
+using std::set;
using std::shared_ptr;
+using std::sort;
using std::vector;
-namespace {
-bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
- const vector<int>& nonAdditiveFields) {
- const auto& l_values = lhs->getValues();
- const auto& r_values = rhs->getValues();
-
- for (size_t i : nonAdditiveFields) {
- // We store everything starting from index 0, so we need to use i-1
- if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
- l_values[i - 1].mValue == r_values[i - 1].mValue)) {
- return false;
- }
- }
- return true;
-}
-
-// merge rhs to lhs
-// when calling this function, all sanity check should be done already.
-// e.g., index boundary, nonAdditiveFields matching etc.
-bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
- const vector<int>& additiveFields) {
- vector<FieldValue>* host_values = lhs->getMutableValues();
- const auto& child_values = rhs->getValues();
- for (int i : additiveFields) {
- Value& host = (*host_values)[i - 1].mValue;
- const Value& child = (child_values[i - 1]).mValue;
- if (child.getType() != host.getType()) {
- return false;
- }
- switch (child.getType()) {
- case INT:
- host.setInt(host.int_value + child.int_value);
- break;
- case LONG:
- host.setLong(host.long_value + child.long_value);
- break;
- default:
- ALOGE("Tried to merge 2 fields with unsupported type");
- return false;
- }
- }
- return true;
-}
-
-bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
- const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
- for (const auto& pos : host_pos) {
- if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
- mergeEvent(data[pos], data[child_pos], additiveFields)) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
/**
* Process all data and merge isolated with host if necessary.
* For example:
@@ -95,7 +41,7 @@
* int byte_send = 3;
* int byte_recv = 4;
* }
- * additive fields are {3, 4}, non-additive field is {2}
+ * additive fields are {3, 4}
* If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
* [uid1, fg, 100, 200]
* [uid1_child, fg, 100, 200]
@@ -104,65 +50,119 @@
* We want to merge them and results should be:
* [uid1, fg, 200, 400]
* [uid1, bg, 100, 200]
+ *
+ * All atoms should be of the same tagId. All fields should be present.
*/
-void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
- int tagId) {
+void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
+ int tagId) {
if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
StatsPullerManager::kAllPullAtomInfo.end()) {
VLOG("Unknown pull atom id %d", tagId);
return;
}
- int uidField;
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
- if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
- VLOG("No uid to merge for atom %d", tagId);
+ if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
+ (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+ android::util::AtomsInfo::kAtomsWithUidField.end())) {
+ VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
- } else {
- uidField = it->second; // uidField is the field number in proto,
}
- const vector<int>& additiveFields =
- StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
- const vector<int>& nonAdditiveFields =
- StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
- // map of host uid to their position in the original vector
- map<int, vector<int>> hostPosition;
- vector<bool> toRemove = vector<bool>(data.size(), false);
-
- for (size_t i = 0; i < data.size(); i++) {
- vector<FieldValue>* valueList = data[i]->getMutableValues();
-
- int uid;
- if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
- (data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
- uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
- } else {
- ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
- continue;
+ // 1. Map all isolated uid in-place to host uid
+ for (shared_ptr<LogEvent>& event : data) {
+ if (event->GetTagId() != tagId) {
+ ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
+ return;
}
-
- const int hostUid = uidMap->getHostUidOrSelf(uid);
-
- if (hostUid != uid) {
- (*valueList)[0].mValue.setInt(hostUid);
- }
- if (hostPosition.find(hostUid) == hostPosition.end()) {
- hostPosition[hostUid].push_back(i);
+ if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
+ for (auto& value : *(event->getMutableValues())) {
+ if (value.mField.getPosAtDepth(0) > kAttributionField) {
+ break;
+ }
+ if (isAttributionUidField(value)) {
+ const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
+ value.mValue.setInt(hostUid);
+ }
+ }
} else {
- if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
- toRemove[i] = true;
- } else {
- hostPosition[hostUid].push_back(i);
+ auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
+ if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+ int uidField = it->second; // uidField is the field number in proto,
+ // starting from 1
+ if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+ (event->getValues())[uidField - 1].mValue.getType() == INT) {
+ Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+ const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+ return;
+ }
}
}
}
+ // 2. sort the data, bit-wise
+ sort(data.begin(), data.end(),
+ [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) {
+ if (lhs->size() != rhs->size()) {
+ return lhs->size() < rhs->size();
+ }
+ const std::vector<FieldValue>& lhsValues = lhs->getValues();
+ const std::vector<FieldValue>& rhsValues = rhs->getValues();
+ for (int i = 0; i < (int)lhs->size(); i++) {
+ if (lhsValues[i] != rhsValues[i]) {
+ return lhsValues[i] < rhsValues[i];
+ }
+ }
+ return false;
+ });
+
vector<shared_ptr<LogEvent>> mergedData;
- for (size_t i = 0; i < toRemove.size(); i++) {
- if (!toRemove[i]) {
+ const vector<int>& additiveFieldsVec =
+ StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+ const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
+ bool needMerge = true;
+
+ // 3. do the merge.
+ // The loop invariant is this: for every event, check if it differs on
+ // non-additive fields, or have different attribution chain length.
+ // If so, no need to merge, add itself to the result.
+ // Otherwise, merge the value onto the one immediately next to it.
+ for (int i = 0; i < (int)data.size() - 1; i++) {
+ // Size different, must be different chains.
+ if (data[i]->size() != data[i + 1]->size()) {
mergedData.push_back(data[i]);
+ continue;
+ }
+ vector<FieldValue>* lhsValues = data[i]->getMutableValues();
+ vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues();
+ needMerge = true;
+ for (int p = 0; p < (int)lhsValues->size(); p++) {
+ if ((*lhsValues)[p] != (*rhsValues)[p]) {
+ int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+ // Differ on non-additive field, abort.
+ if (additiveFields.find(pos) == additiveFields.end()) {
+ needMerge = false;
+ break;
+ }
+ }
+ }
+ if (!needMerge) {
+ mergedData.push_back(data[i]);
+ continue;
+ }
+ // This should be infrequent operation.
+ for (int p = 0; p < (int)lhsValues->size(); p++) {
+ int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+ if (additiveFields.find(pos) != additiveFields.end()) {
+ (*rhsValues)[p].mValue += (*lhsValues)[p].mValue;
+ }
}
}
+ mergedData.push_back(data.back());
+
data.clear();
data = mergedData;
}
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index fd4a4a2..f703e6c 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -25,8 +25,8 @@
namespace os {
namespace statsd {
-void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
- const sp<UidMap>& uidMap, int tagId);
+void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 6617689..3e5e82f 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -101,6 +101,7 @@
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
{android::util::BINDER_CALLS, {6000, 10000}},
+ {android::util::LOOPER_STATS, {1500, 2500}},
{android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 343709a..3157037 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -113,7 +113,7 @@
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
- static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+ static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
// Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
// data subscriber that it's time to call getData.
@@ -130,7 +130,7 @@
static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
/* Min period between two checks of byte size per config key in nanoseconds. */
- static const int64_t kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC;
+ static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
// Maximum age (30 days) that files on disk can exist in seconds.
static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 625294c..8d61aba 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,13 +41,28 @@
}
}
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
mTagId = statsLogEventWrapper.getTagId();
mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
mLogUid = 0;
+ int workChainPosOffset = 0;
+ if (workChainIndex != -1) {
+ const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
+ // chains are at field 1, level 2
+ int depth = 2;
+ for (int i = 0; i < (int)wc.uids.size(); i++) {
+ int pos[] = {1, i + 1, 1};
+ mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
+ pos[2]++;
+ mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
+ mValues.back().mField.decorateLastPos(2);
+ }
+ mValues.back().mField.decorateLastPos(1);
+ workChainPosOffset = 1;
+ }
for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
- Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+ Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
switch (statsLogEventWrapper.getElements()[i].type) {
case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
mValues.push_back(
@@ -79,6 +94,17 @@
}
}
+void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+ std::vector<std::shared_ptr<LogEvent>>& logEvents) {
+ if (statsLogEventWrapper.getWorkChains().size() == 0) {
+ logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
+ } else {
+ for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
+ logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
+ }
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mTagId = tagId;
@@ -653,7 +679,7 @@
string LogEvent::ToString() const {
string result;
- result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs,
+ result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
(long long)mElapsedTimestampNs, mTagId);
for (const auto& value : mValues) {
result +=
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3d5b2ab..5408d17 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -65,7 +65,16 @@
*/
explicit LogEvent(log_msg& msg);
- explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+ /**
+ * Creates LogEvent from StatsLogEventWrapper.
+ */
+ static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+ std::vector<std::shared_ptr<LogEvent>>& logEvents);
+
+ /**
+ * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
+ */
+ explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c3912ee..9fe84dc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -165,7 +165,7 @@
const bool mSkipZeroDiffOutput;
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4ac55b5..b317361 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -454,6 +454,16 @@
ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
return false;
}
+ if (!metric.has_value_field()) {
+ ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return false;
+ }
+ std::vector<Matcher> fieldMatchers;
+ translateFieldMatcher(metric.value_field(), &fieldMatchers);
+ if (fieldMatchers.size() < 1) {
+ ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return false;
+ }
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.id(), metricIndex});
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 504c586..f1310db 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -343,9 +343,11 @@
}
}
if (isBytesField) {
- protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
- (const char*)dim.mValue.str_value.c_str(),
- dim.mValue.str_value.length());
+ if (dim.mValue.str_value.length() > 0) {
+ protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+ (const char*)dim.mValue.str_value.c_str(),
+ dim.mValue.str_value.length());
+ }
} else {
protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6384757..3a5be43 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -394,6 +394,167 @@
EXPECT_EQ(1.1f, item16.mValue.float_value);
}
+TEST(LogEventTest, TestStatsLogEventWrapperNoChain) {
+ Parcel parcel;
+ // tag id
+ parcel.writeInt32(1);
+ // elapsed realtime
+ parcel.writeInt64(1111L);
+ // wallclock time
+ parcel.writeInt64(2222L);
+ // no chain
+ parcel.writeInt32(0);
+ // 2 data
+ parcel.writeInt32(2);
+ // int 6
+ parcel.writeInt32(1);
+ parcel.writeInt32(6);
+ // long 10
+ parcel.writeInt32(2);
+ parcel.writeInt64(10);
+ parcel.setDataPosition(0);
+
+ StatsLogEventWrapper statsLogEventWrapper;
+ EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+ EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+ EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+ EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+ EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size());
+ EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+ EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+ EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+ LogEvent event(statsLogEventWrapper, -1);
+ EXPECT_EQ(1, event.GetTagId());
+ EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+ EXPECT_EQ(2, event.size());
+ EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+ EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+}
+
+TEST(LogEventTest, TestStatsLogEventWrapperWithChain) {
+ Parcel parcel;
+ // tag id
+ parcel.writeInt32(1);
+ // elapsed realtime
+ parcel.writeInt64(1111L);
+ // wallclock time
+ parcel.writeInt64(2222L);
+ // 3 chains
+ parcel.writeInt32(3);
+ // chain1, 2 nodes (1, "tag1") (2, "tag2")
+ parcel.writeInt32(2);
+ parcel.writeInt32(1);
+ parcel.writeString16(String16("tag1"));
+ parcel.writeInt32(2);
+ parcel.writeString16(String16("tag2"));
+ // chain2, 1 node (3, "tag3")
+ parcel.writeInt32(1);
+ parcel.writeInt32(3);
+ parcel.writeString16(String16("tag3"));
+ // chain3, 2 nodes (4, "") (5, "")
+ parcel.writeInt32(2);
+ parcel.writeInt32(4);
+ parcel.writeString16(String16(""));
+ parcel.writeInt32(5);
+ parcel.writeString16(String16(""));
+ // 2 data
+ parcel.writeInt32(2);
+ // int 6
+ parcel.writeInt32(1);
+ parcel.writeInt32(6);
+ // long 10
+ parcel.writeInt32(2);
+ parcel.writeInt64(10);
+ parcel.setDataPosition(0);
+
+ StatsLogEventWrapper statsLogEventWrapper;
+ EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+ EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+ EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+ EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+ EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size());
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size());
+ EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size());
+ EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]);
+ EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]);
+ EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size());
+ EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]);
+ EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size());
+ EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]);
+ EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+ EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+ EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size());
+ EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]);
+ EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size());
+ EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]);
+ EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]);
+
+ LogEvent event(statsLogEventWrapper, -1);
+ EXPECT_EQ(1, event.GetTagId());
+ EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+ EXPECT_EQ(2, event.size());
+ EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+ EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+
+ LogEvent event1(statsLogEventWrapper, 0);
+
+ EXPECT_EQ(1, event1.GetTagId());
+ EXPECT_EQ(1111L, event1.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event1.GetLogdTimestampNs());
+ EXPECT_EQ(6, event1.size());
+ EXPECT_EQ(1, event1.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField());
+ EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value);
+ EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField());
+ EXPECT_EQ(2, event1.getValues()[2].mValue.int_value);
+ EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField());
+ EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value);
+ EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField());
+ EXPECT_EQ(6, event1.getValues()[4].mValue.int_value);
+ EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField());
+ EXPECT_EQ(10, event1.getValues()[5].mValue.long_value);
+ EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField());
+
+ LogEvent event2(statsLogEventWrapper, 1);
+
+ EXPECT_EQ(1, event2.GetTagId());
+ EXPECT_EQ(1111L, event2.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event2.GetLogdTimestampNs());
+ EXPECT_EQ(4, event2.size());
+ EXPECT_EQ(3, event2.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField());
+ EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value);
+ EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField());
+ EXPECT_EQ(6, event2.getValues()[2].mValue.int_value);
+ EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField());
+ EXPECT_EQ(10, event2.getValues()[3].mValue.long_value);
+ EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField());
+
+ LogEvent event3(statsLogEventWrapper, 2);
+
+ EXPECT_EQ(1, event3.GetTagId());
+ EXPECT_EQ(1111L, event3.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event3.GetLogdTimestampNs());
+ EXPECT_EQ(6, event3.size());
+ EXPECT_EQ(4, event3.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField());
+ EXPECT_EQ("", event3.getValues()[1].mValue.str_value);
+ EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField());
+ EXPECT_EQ(5, event3.getValues()[2].mValue.int_value);
+ EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField());
+ EXPECT_EQ("", event3.getValues()[3].mValue.str_value);
+ EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField());
+ EXPECT_EQ(6, event3.getValues()[4].mValue.int_value);
+ EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField());
+ EXPECT_EQ(10, event3.getValues()[5].mValue.long_value);
+ EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField());
+}
TEST(LogEventTest, TestBinaryFieldAtom) {
Atom launcherAtom;
@@ -444,7 +605,44 @@
EXPECT_EQ(orig_str, result_str);
}
+TEST(LogEventTest, TestBinaryFieldAtom_empty) {
+ Atom launcherAtom;
+ auto launcher_event = launcherAtom.mutable_launcher_event();
+ launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+ launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+ launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+ // empty string.
+ string extension_str;
+
+ LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+ event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+ event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+ event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+ event1.write(extension_str);
+ event1.init();
+
+ ProtoOutputStream proto;
+ event1.ToProto(proto);
+
+ std::vector<uint8_t> outData;
+ outData.resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ std::string result_str(outData.begin(), outData.end());
+ std::string orig_str;
+ launcherAtom.SerializeToString(&orig_str);
+
+ EXPECT_EQ(orig_str, result_str);
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index fc6e420..266ea35 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -80,7 +80,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
vector<vector<int>> actual;
extractIntoVector(inputData, actual);
@@ -120,7 +120,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
vector<vector<int>> actual;
extractIntoVector(inputData, actual);
@@ -154,7 +154,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
// 20->32->31
// 20->22->21
@@ -190,7 +190,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
// 20->32->31
// 20->22->21
@@ -231,7 +231,7 @@
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
vector<vector<int>> actual;
extractIntoVector(inputData, actual);
@@ -256,7 +256,7 @@
inputData.push_back(event);
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
EXPECT_EQ(2, (int)inputData.size());
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 8e9b91d..f3bf6e7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,10 @@
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_SCREENING_APP = "set-test-call-screening-app";
+ private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
+ "add-or-remove-call-companion-app";
+ private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,6 +68,9 @@
"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-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" +
"usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
"usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom set-default-dialer <PACKAGE>\n" +
@@ -113,6 +120,15 @@
case COMMAND_REGISTER_PHONE_ACCOUNT:
runRegisterPhoneAccount();
break;
+ case COMMAND_SET_TEST_CALL_SCREENING_APP:
+ runSetTestCallScreeningApp();
+ break;
+ case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
+ runAddOrRemoveCallCompanionApp();
+ break;
+ case COMMAND_SET_TEST_AUTO_MODE_APP:
+ runSetTestAutoModeApp();
+ break;
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
@@ -173,6 +189,23 @@
System.out.println("Success - " + handle + " registered.");
}
+ private void runSetTestCallScreeningApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestDefaultCallScreeningApp(packageName);
+ }
+
+ private void runAddOrRemoveCallCompanionApp() throws RemoteException {
+ final String packageName = nextArgRequired();
+ String isAdded = nextArgRequired();
+ boolean isAddedBool = "1".equals(isAdded);
+ mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
+ }
+
+ private void runSetTestAutoModeApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestAutoModeApp(packageName);
+ }
+
private void runUnregisterPhoneAccount() throws RemoteException {
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
mTelecomService.unregisterPhoneAccount(handle);
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 2dcf50f..c2e441b 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -24296,7 +24296,8 @@
HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$RangeOption;ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
HSPLandroid/icu/util/CodePointMap;->iterator()Ljava/util/Iterator;
-HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;HSPLandroid/icu/util/Currency$1;-><init>()V
+HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;
+HSPLandroid/icu/util/Currency$1;-><init>()V
HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/String;Ljava/lang/Void;)Landroid/icu/util/Currency;
HSPLandroid/icu/util/Currency$CurrencyUsage;-><init>(Ljava/lang/String;I)V
@@ -32940,8 +32941,8 @@
HSPLandroid/view/IWindowManager;->isWindowTraceEnabled()Z
HSPLandroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
HSPLandroid/view/IWindowManager;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z;I)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
HSPLandroid/view/IWindowManager;->prepareAppTransition(IZ)V
HSPLandroid/view/IWindowManager;->reenableKeyguard(Landroid/os/IBinder;)V
HSPLandroid/view/IWindowManager;->refreshScreenCaptureDisabled(I)V
@@ -41154,7 +41155,7 @@
HSPLcom/android/internal/telephony/TimeServiceHelper;->setListener(Lcom/android/internal/telephony/TimeServiceHelper$Listener;)V
HSPLcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;->toString()Ljava/lang/String;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;->toString()Ljava/lang/String;
-HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
+HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByCountry(Ljava/lang/String;J)Lcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByNitzCountry(Lcom/android/internal/telephony/NitzData;Ljava/lang/String;)Lcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;
HSPLcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastRangeForSubscriber(IIII)Z
@@ -51930,7 +51931,46 @@
HSPLlibcore/reflect/Types;->getTypeArray(Llibcore/reflect/ListOfTypes;Z)[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/WildcardTypeImpl;->getLowerBounds()[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/WildcardTypeImpl;->getUpperBounds()[Ljava/lang/reflect/Type;
-HSPLlibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
+HSPLlibcore/timezone/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
+HSPLlibcore/timezone/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
+HSPLlibcore/timezone/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
+HSPLlibcore/timezone/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/timezone/CountryTimeZones$OffsetResult;
+HSPLlibcore/timezone/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
+HSPLlibcore/timezone/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
+HSPLlibcore/timezone/TimeZoneFinder;->getInstance()Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
+HSPLlibcore/timezone/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
+HSPLlibcore/timezone/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
+HSPLlibcore/timezone/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->processXml(Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->close()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->finalize()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/timezone/ZoneInfoDB$TzData;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readHeader()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->validateOffset(II)V
+HSPLlibcore/timezone/ZoneInfoDB;->getInstance()Llibcore/timezone/ZoneInfoDB$TzData;
HSPLlibcore/util/BasicLruCache;-><init>(I)V
HSPLlibcore/util/BasicLruCache;->create(Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/BasicLruCache;->entryEvicted(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -51938,12 +51978,6 @@
HSPLlibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/CollectionUtils;->removeDuplicates(Ljava/util/List;Ljava/util/Comparator;)V
-HSPLlibcore/util/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
-HSPLlibcore/util/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
-HSPLlibcore/util/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
-HSPLlibcore/util/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
-HSPLlibcore/util/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/util/CountryTimeZones$OffsetResult;
HSPLlibcore/util/HexEncoding;->encode([BII)[C
HSPLlibcore/util/NativeAllocationRegistry$CleanerRunner;->run()V
HSPLlibcore/util/NativeAllocationRegistry$CleanerThunk;->run()V
@@ -51951,23 +51985,6 @@
HSPLlibcore/util/NativeAllocationRegistry;->registerNativeAllocation(Ljava/lang/Object;J)Ljava/lang/Runnable;
HSPLlibcore/util/SneakyThrow;->sneakyThrow(Ljava/lang/Throwable;)V
HSPLlibcore/util/SneakyThrow;->sneakyThrow_(Ljava/lang/Throwable;)V
-HSPLlibcore/util/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
-HSPLlibcore/util/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/util/TimeZoneFinder$ReaderSupplier;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
-HSPLlibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
-HSPLlibcore/util/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
-HSPLlibcore/util/TimeZoneFinder;->getInstance()Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
-HSPLlibcore/util/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
-HSPLlibcore/util/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
-HSPLlibcore/util/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)Z
-HSPLlibcore/util/TimeZoneFinder;->processXml(Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)V
HSPLlibcore/util/ZoneInfo$WallTime;-><init>()V
HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsFromCalendar()V
HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsToCalendar()V
@@ -52007,22 +52024,6 @@
HSPLlibcore/util/ZoneInfo;->hashCode()I
HSPLlibcore/util/ZoneInfo;->inDaylightTime(Ljava/util/Date;)Z
HSPLlibcore/util/ZoneInfo;->readTimeZone(Ljava/lang/String;Llibcore/io/BufferIterator;J)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->close()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->finalize()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
-HSPLlibcore/util/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
-HSPLlibcore/util/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/util/ZoneInfoDB$TzData;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->readHeader()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->validateOffset(II)V
-HSPLlibcore/util/ZoneInfoDB;->getInstance()Llibcore/util/ZoneInfoDB$TzData;
HSPLorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->putString(Ljava/nio/ByteBuffer;Ljava/lang/String;)V
HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->type(Ljava/lang/String;)I
@@ -63455,13 +63456,21 @@
Llibcore/reflect/TypeVariableImpl;
Llibcore/reflect/Types;
Llibcore/reflect/WildcardTypeImpl;
-Llibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/CountryTimeZones$OffsetResult;
+Llibcore/timezone/CountryTimeZones$TimeZoneMapping;
+Llibcore/timezone/CountryTimeZones;
+Llibcore/timezone/TimeZoneDataFiles;
+Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+Llibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
+Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;
+Llibcore/timezone/TimeZoneFinder;
+Llibcore/timezone/ZoneInfoDB$TzData$1;
+Llibcore/timezone/ZoneInfoDB$TzData;
+Llibcore/timezone/ZoneInfoDB;
Llibcore/util/BasicLruCache;
Llibcore/util/CharsetUtils;
Llibcore/util/CollectionUtils;
-Llibcore/util/CountryTimeZones$OffsetResult;
-Llibcore/util/CountryTimeZones$TimeZoneMapping;
-Llibcore/util/CountryTimeZones;
Llibcore/util/EmptyArray;
Llibcore/util/HexEncoding;
Llibcore/util/NativeAllocationRegistry$CleanerRunner;
@@ -63469,18 +63478,10 @@
Llibcore/util/NativeAllocationRegistry;
Llibcore/util/Objects;
Llibcore/util/SneakyThrow;
-Llibcore/util/TimeZoneDataFiles;
-Llibcore/util/TimeZoneFinder$ReaderSupplier;
-Llibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
-Llibcore/util/TimeZoneFinder$TimeZonesProcessor;
-Llibcore/util/TimeZoneFinder;
Llibcore/util/ZoneInfo$CheckedArithmeticException;
Llibcore/util/ZoneInfo$OffsetInterval;
Llibcore/util/ZoneInfo$WallTime;
Llibcore/util/ZoneInfo;
-Llibcore/util/ZoneInfoDB$TzData$1;
-Llibcore/util/ZoneInfoDB$TzData;
-Llibcore/util/ZoneInfoDB;
Lorg/apache/harmony/dalvik/NativeTestTarget;
Lorg/apache/harmony/dalvik/ddmc/Chunk;
Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;
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 87%
rename from config/hiddenapi-light-greylist.txt
rename to config/hiddenapi-greylist.txt
index 8a770b9..01c7028 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;
@@ -189,10 +210,10 @@
Landroid/app/IUiModeManager;->disableCarMode(I)V
Landroid/app/IUserSwitchObserver$Stub;-><init>()V
Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
-Landroid/app/IWallpaperManager;->getHeightHint()I
+Landroid/app/IWallpaperManager;->getHeightHint(I)I
Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
Landroid/app/IWallpaperManager;->getWallpaperInfo(I)Landroid/app/WallpaperInfo;
-Landroid/app/IWallpaperManager;->getWidthHint()I
+Landroid/app/IWallpaperManager;->getWidthHint(I)I
Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z
Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V
Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V
@@ -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;
@@ -1435,10 +1580,13 @@
Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
-Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
+Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar(I)Z
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,21 +1595,24 @@
Landroid/view/IWindowManager;->getDockedStackSide()I
Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
-Landroid/view/IWindowManager;->hasNavigationBar()Z
+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
Landroid/view/IWindowManager;->thawRotation()V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
Landroid/view/IWindowSession;->getInTouchMode()Z
@@ -1488,91 +1639,16 @@
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/AsYouTypeFormatter;->clear()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->getRememberedPosition()I
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigit(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigitAndRememberPosition(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getDescriptionForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/util/Locale;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getInstance()Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;
-Lcom/android/i18n/phonenumbers/NumberParseException;->getErrorType()Lcom/android/i18n/phonenumbers/NumberParseException$ErrorType;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getDomesticCarrierCodeFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getFormat()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getLeadingDigitsPattern(I)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getNationalPrefixFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->leadingDigitsPatternSize()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getGeneralDesc()Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixForParsing()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixTransformRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getPreferredExtnPrefix()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasNationalPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasPreferredExtnPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->intlNumberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->numberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;->getMetadataList()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getNationalNumberPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_DEFAULT_COUNTRY:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITHOUT_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_IDD:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->clearCountryCode()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCodeSource()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getExtension()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getNationalNumber()J
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasCountryCode()Z
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasExtension()Z
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->end()I
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->number()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->rawString()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->start()I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;->POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->EXACT_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NOT_A_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NO_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->SHORT_NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->E164:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->INTERNATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->NATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->RFC3966:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE_OR_MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PAGER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PERSONAL_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PREMIUM_RATE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->SHARED_COST:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->TOLL_FREE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->UAN:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOICEMAIL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOIP:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->IS_POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->TOO_LONG:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->findNumbers(Ljava/lang/CharSequence;Ljava/lang/String;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;J)Ljava/lang/Iterable;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->format(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->formatInOriginalFormat(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getAsYouTypeFormatter(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getCountryCodeForRegion(Ljava/lang/String;)I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getInstance()Lcom/android/i18n/phonenumbers/PhoneNumberUtil;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNationalSignificantNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNumberType(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getRegionCodeForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isNumberMatch(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumberWithReason(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isValidNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
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;
@@ -1582,12 +1658,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
@@ -1598,7 +1710,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;
@@ -1629,6 +1786,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
@@ -1676,6 +1834,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;
@@ -1689,6 +1848,14 @@
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;->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/logging/MetricsLogger;-><init>()V
Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
Lcom/android/internal/net/VpnConfig;-><init>()V
@@ -2058,6 +2225,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
@@ -2183,6 +2351,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
@@ -2691,7 +2860,6 @@
Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
@@ -2736,8 +2904,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;
@@ -2768,17 +2934,14 @@
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
Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerForAllDataDisconnected(Landroid/os/Handler;ILjava/lang/Object;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
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
@@ -2787,7 +2950,6 @@
Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->updateRecords()V
Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -3206,9 +3368,9 @@
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;->getVoiceMessageCount()I
Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
@@ -3279,7 +3441,6 @@
Lcom/android/internal/telephony/Phone;->isWifiCallingEnabled()Z
Lcom/android/internal/telephony/Phone;->mCi:Lcom/android/internal/telephony/CommandsInterface;
Lcom/android/internal/telephony/Phone;->mContext:Landroid/content/Context;
-Lcom/android/internal/telephony/Phone;->mDcTracker:Lcom/android/internal/telephony/dataconnection/DcTracker;
Lcom/android/internal/telephony/Phone;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
Lcom/android/internal/telephony/Phone;->mImsPhone:Lcom/android/internal/telephony/Phone;
Lcom/android/internal/telephony/Phone;->mMmiRegistrants:Landroid/os/RegistrantList;
@@ -3372,7 +3533,6 @@
Lcom/android/internal/telephony/ProxyController;->mRadioCapabilitySessionId:I
Lcom/android/internal/telephony/ProxyController;->mSetRadioAccessFamilyStatus:[I
Lcom/android/internal/telephony/ProxyController;->mUniqueIdGenerator:Ljava/util/concurrent/atomic/AtomicInteger;
-Lcom/android/internal/telephony/ProxyController;->registerForAllDataDisconnected(ILandroid/os/Handler;ILjava/lang/Object;)V
Lcom/android/internal/telephony/ProxyController;->sendRadioCapabilityRequest(IIIILjava/lang/String;II)V
Lcom/android/internal/telephony/ProxyController;->sProxyController:Lcom/android/internal/telephony/ProxyController;
Lcom/android/internal/telephony/RadioCapability;->getRadioAccessFamily()I
@@ -3472,7 +3632,6 @@
Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V
Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z
Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V
-Lcom/android/internal/telephony/ServiceStateTracker;->powerOffRadioSafely(Lcom/android/internal/telephony/dataconnection/DcTracker;)V
Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V
Lcom/android/internal/telephony/ServiceStateTracker;->resetServiceStateInIwlanMode()V
Lcom/android/internal/telephony/ServiceStateTracker;->setOperatorIdd(Ljava/lang/String;)V
@@ -4139,77 +4298,6 @@
Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrollStateChanged(I)V
Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageSelected(I)V
Lcom/android/internal/widget/ViewPager;->getCurrentItem()I
-Lcom/android/okhttp/Connection;->getSocket()Ljava/net/Socket;
-Lcom/android/okhttp/ConnectionPool;->connections:Ljava/util/Deque;
-Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
-Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
-Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/HttpHandler;-><init>()V
-Lcom/android/okhttp/HttpsHandler;-><init>()V
-Lcom/android/okhttp/HttpUrl$Builder;->build()Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
-Lcom/android/okhttp/HttpUrl;->newBuilder()Lcom/android/okhttp/HttpUrl$Builder;
-Lcom/android/okhttp/HttpUrl;->parse(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HeaderParser;->skipUntil(Ljava/lang/String;ILjava/lang/String;)I
-Lcom/android/okhttp/internal/http/HeaderParser;->skipWhitespace(Ljava/lang/String;I)I
-Lcom/android/okhttp/internal/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Lcom/android/okhttp/internal/http/HttpEngine;->getConnection()Lcom/android/okhttp/Connection;
-Lcom/android/okhttp/internal/http/HttpEngine;->hasResponse()Z
-Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest:Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->readResponse()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sendRequest()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sentRequestMillis:J
-Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->writingRequestHeaders()V
-Lcom/android/okhttp/internal/http/RouteSelector;->hasNext()Z
-Lcom/android/okhttp/internal/huc/HttpsURLConnectionImpl;->delegate:Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->client:Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->httpEngine:Lcom/android/okhttp/internal/http/HttpEngine;
-Lcom/android/okhttp/internal/Internal;-><init>()V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->apply(Lcom/android/okhttp/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V
-Lcom/android/okhttp/internal/Internal;->callEngineGetStreamAllocation(Lcom/android/okhttp/Call;)Lcom/android/okhttp/internal/http/StreamAllocation;
-Lcom/android/okhttp/internal/Internal;->callEnqueue(Lcom/android/okhttp/Call;Lcom/android/okhttp/Callback;Z)V
-Lcom/android/okhttp/internal/Internal;->connectionBecameIdle(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)Z
-Lcom/android/okhttp/internal/Internal;->get(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/Address;Lcom/android/okhttp/internal/http/StreamAllocation;)Lcom/android/okhttp/internal/io/RealConnection;
-Lcom/android/okhttp/internal/Internal;->getHttpUrlChecked(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/internal/Internal;->instance:Lcom/android/okhttp/internal/Internal;
-Lcom/android/okhttp/internal/Internal;->internalCache(Lcom/android/okhttp/OkHttpClient;)Lcom/android/okhttp/internal/InternalCache;
-Lcom/android/okhttp/internal/Internal;->put(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)V
-Lcom/android/okhttp/internal/Internal;->routeDatabase(Lcom/android/okhttp/ConnectionPool;)Lcom/android/okhttp/internal/RouteDatabase;
-Lcom/android/okhttp/internal/Internal;->setCache(Lcom/android/okhttp/OkHttpClient;Lcom/android/okhttp/internal/InternalCache;)V
-Lcom/android/okhttp/internal/Platform;->get()Lcom/android/okhttp/internal/Platform;
-Lcom/android/okhttp/internal/Platform;->logW(Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Util;->closeAll(Ljava/io/Closeable;Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->closeQuietly(Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->EMPTY_BYTE_ARRAY:[B
-Lcom/android/okhttp/internal/Util;->UTF_8:Ljava/nio/charset/Charset;
-Lcom/android/okhttp/OkHttpClient;-><init>()V
-Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
-Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
-Lcom/android/okhttp/OkHttpClient;->getConnectionPool()Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->getCookieHandler()Ljava/net/CookieHandler;
-Lcom/android/okhttp/OkHttpClient;->getHostnameVerifier()Ljavax/net/ssl/HostnameVerifier;
-Lcom/android/okhttp/OkHttpClient;->getProxy()Ljava/net/Proxy;
-Lcom/android/okhttp/OkHttpClient;->getProxySelector()Ljava/net/ProxySelector;
-Lcom/android/okhttp/OkHttpClient;->getSslSocketFactory()Ljavax/net/ssl/SSLSocketFactory;
-Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
-Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Request;->method:Ljava/lang/String;
-Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/Response;->code:I
-Lcom/android/okhttp/Response;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Response;->message:Ljava/lang/String;
-Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
@@ -4515,7 +4603,6 @@
Lcom/google/android/util/AbstractMessageParser$Token$Type;->SMILEY:Lcom/google/android/util/AbstractMessageParser$Token$Type;
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
Lcom/google/android/util/AbstractMessageParser$Token$Type;->YOUTUBE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
-Lcom/sun/nio/file/ExtendedWatchEventModifier;->FILE_TREE:Lcom/sun/nio/file/ExtendedWatchEventModifier;
Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V
Lgov/nist/core/GenericObject;-><init>()V
Lgov/nist/core/GenericObject;->dbgPrint()V
@@ -4655,178 +4742,9 @@
Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V
Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V
Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri;
-Ljava/lang/DexCache;->dexFile:J
-Ljava/lang/invoke/SerializedLambda;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
-Ljava/lang/invoke/SerializedLambda;->getCapturedArg(I)Ljava/lang/Object;
-Ljava/lang/invoke/SerializedLambda;->getCapturedArgCount()I
-Ljava/lang/invoke/SerializedLambda;->getCapturingClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodKind()I
-Ljava/lang/invoke/SerializedLambda;->getImplMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getInstantiatedMethodType()Ljava/lang/String;
-Ljava/lang/UNIXProcess;->pid:I
-Ljava/net/AddressCache$AddressCacheEntry;-><init>(Ljava/lang/Object;)V
-Ljava/net/AddressCache$AddressCacheEntry;->expiryNanos:J
-Ljava/net/AddressCache$AddressCacheEntry;->value:Ljava/lang/Object;
-Ljava/net/AddressCache$AddressCacheKey;->mHostname:Ljava/lang/String;
-Ljava/net/AddressCache;->cache:Llibcore/util/BasicLruCache;
-Ljava/net/Inet6AddressImpl;->addressCache:Ljava/net/AddressCache;
-Ljava/net/PlainSocketImpl;-><init>()V
-Ljava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner;
-Ljava/nio/file/FileTreeWalker;->followLinks:Z
-Ljava/nio/file/FileTreeWalker;->linkOptions:[Ljava/nio/file/LinkOption;
-Ljava/nio/file/FileTreeWalker;->maxDepth:I
-Ljava/util/zip/ZipFile$ZipEntryIterator;->nextElement()Ljava/util/zip/ZipEntry;
Ljunit/framework/TestCase;->fName:Ljava/lang/String;
Ljunit/framework/TestSuite;->isPublicTestMethod(Ljava/lang/reflect/Method;)Z
Ljunit/framework/TestSuite;->isTestMethod(Ljava/lang/reflect/Method;)Z
-Llibcore/icu/DateIntervalFormat;->formatDateRange(JJILjava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->CACHED_PATTERNS:Llibcore/util/BasicLruCache;
-Llibcore/icu/ICU;->getBestDateTimePattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
-Llibcore/icu/ICU;->getBestDateTimePatternNative(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->getDateFormatOrder(Ljava/lang/String;)[C
-Llibcore/icu/LocaleData;->firstDayOfWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->get(Ljava/util/Locale;)Llibcore/icu/LocaleData;
-Llibcore/icu/LocaleData;->longStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/icu/LocaleData;->minimalDaysInFirstWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->shortMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_Hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->today:Ljava/lang/String;
-Llibcore/icu/LocaleData;->tomorrow:Ljava/lang/String;
-Llibcore/icu/LocaleData;->zeroDigit:C
-Llibcore/icu/TimeZoneNames;->forLocale(Ljava/util/Locale;)[Ljava/lang/String;
-Llibcore/io/AsynchronousCloseMonitor;->signalBlockedThreads(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fchmod(Ljava/io/FileDescriptor;I)V
-Llibcore/io/BlockGuardOs;->fchown(Ljava/io/FileDescriptor;II)V
-Llibcore/io/BlockGuardOs;->fdatasync(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fstat(Ljava/io/FileDescriptor;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->fstatvfs(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->lseek(Ljava/io/FileDescriptor;JI)J
-Llibcore/io/BlockGuardOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->readv(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BlockGuardOs;->realpath(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->remove(Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->writev(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BufferIterator;->readByte()B
-Llibcore/io/BufferIterator;->readByteArray([BII)V
-Llibcore/io/BufferIterator;->readInt()I
-Llibcore/io/BufferIterator;->readIntArray([III)V
-Llibcore/io/BufferIterator;->seek(I)V
-Llibcore/io/BufferIterator;->skip(I)V
-Llibcore/io/DropBox;->addText(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z
-Llibcore/io/ForwardingOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->getenv(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/ForwardingOs;->os:Llibcore/io/Os;
-Llibcore/io/ForwardingOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->remove(Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->removexattr(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/ForwardingOs;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/ForwardingOs;->setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V
-Llibcore/io/ForwardingOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/ForwardingOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->sysconf(I)J
-Llibcore/io/ForwardingOs;->unlink(Ljava/lang/String;)V
-Llibcore/io/IoBridge;->isConnected(Ljava/io/FileDescriptor;Ljava/net/InetAddress;III)Z
-Llibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/lang/AutoCloseable;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/net/Socket;)V
-Llibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B
-Llibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V
-Llibcore/io/MemoryMappedFile;->bigEndianIterator()Llibcore/io/BufferIterator;
-Llibcore/io/MemoryMappedFile;->mmapRO(Ljava/lang/String;)Llibcore/io/MemoryMappedFile;
-Llibcore/io/Os;->chmod(Ljava/lang/String;I)V
-Llibcore/io/Os;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
-Llibcore/io/Os;->gai_strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->remove(Ljava/lang/String;)V
-Llibcore/io/Os;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/Os;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/Os;->strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->sysconf(I)J
-Llibcore/io/Streams;->readAsciiLine(Ljava/io/InputStream;)Ljava/lang/String;
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;)[B
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;[B)V
-Llibcore/io/Streams;->readSingleByte(Ljava/io/InputStream;)I
-Llibcore/io/Streams;->skipAll(Ljava/io/InputStream;)V
-Llibcore/io/Streams;->writeSingleByte(Ljava/io/OutputStream;I)V
-Llibcore/net/event/NetworkEventDispatcher;->addListener(Llibcore/net/event/NetworkEventListener;)V
-Llibcore/net/event/NetworkEventDispatcher;->getInstance()Llibcore/net/event/NetworkEventDispatcher;
-Llibcore/net/event/NetworkEventListener;-><init>()V
-Llibcore/net/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Llibcore/net/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Llibcore/net/MimeUtils;->guessExtensionFromMimeType(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/MimeUtils;->guessMimeTypeFromExtension(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/NetworkSecurityPolicy;->isCleartextTrafficPermitted()Z
-Llibcore/util/BasicLruCache;-><init>(I)V
-Llibcore/util/BasicLruCache;->evictAll()V
-Llibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/EmptyArray;->BYTE:[B
-Llibcore/util/EmptyArray;->INT:[I
-Llibcore/util/EmptyArray;->OBJECT:[Ljava/lang/Object;
-Llibcore/util/ZoneInfoDB$TzData;-><init>()V
-Lorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
-Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;->CHUNK_ORDER:Ljava/nio/ByteOrder;
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->broadcast(I)V
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->sendChunk(Lorg/apache/harmony/dalvik/ddmc/Chunk;)V
-Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;->getThreadStats()[B
-Lorg/apache/harmony/xml/dom/ElementImpl;->localName:Ljava/lang/String;
-Lorg/apache/harmony/xml/ExpatAttributes;-><init>()V
-Lorg/apache/harmony/xml/ExpatParser$EntityParser;->depth:I
-Lorg/apache/harmony/xml/ExpatParser;-><init>(Ljava/lang/String;Lorg/apache/harmony/xml/ExpatReader;ZLjava/lang/String;Ljava/lang/String;)V
-Lorg/apache/harmony/xml/ExpatParser;->append([BII)V
-Lorg/apache/harmony/xml/ExpatParser;->append([CII)V
-Lorg/apache/harmony/xml/ExpatParser;->attributes:Lorg/apache/harmony/xml/ExpatAttributes;
-Lorg/apache/harmony/xml/ExpatParser;->cloneAttributes()Lorg/xml/sax/Attributes;
-Lorg/apache/harmony/xml/ExpatParser;->finish()V
-Lorg/apache/harmony/xml/ExpatParser;->xmlReader:Lorg/apache/harmony/xml/ExpatReader;
-Lorg/apache/harmony/xml/ExpatReader;-><init>()V
-Lorg/apache/harmony/xml/ExpatReader;->contentHandler:Lorg/xml/sax/ContentHandler;
Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node;
Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener;
Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject;
@@ -5505,431 +5423,3 @@
Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutput(Ljava/io/Writer;)V
Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutputProperty(Ljava/lang/String;Ljava/lang/String;)V
Lorg/ccil/cowan/tagsoup/XMLWriter;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/NamespaceSupport$Context;-><init>(Lorg/xml/sax/helpers/NamespaceSupport;)V
-Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;-><init>(Lorg/xml/sax/helpers/ParserAdapter;)V
-Lsun/misc/ASCIICaseInsensitiveComparator;->CASE_INSENSITIVE_ORDER:Ljava/util/Comparator;
-Lsun/misc/ASCIICaseInsensitiveComparator;->lowerCaseHashCode(Ljava/lang/String;)I
-Lsun/misc/BASE64Decoder;-><init>()V
-Lsun/misc/BASE64Decoder;->pem_convert_array:[B
-Lsun/misc/BASE64Encoder;-><init>()V
-Lsun/misc/BASE64Encoder;->pem_array:[C
-Lsun/misc/CEFormatException;-><init>(Ljava/lang/String;)V
-Lsun/misc/CEStreamExhausted;-><init>()V
-Lsun/misc/CharacterDecoder;-><init>()V
-Lsun/misc/CharacterEncoder;-><init>()V
-Lsun/misc/CharacterEncoder;->encodeBuffer([B)Ljava/lang/String;
-Lsun/misc/CharacterEncoder;->encodeBufferPrefix(Ljava/io/OutputStream;)V
-Lsun/misc/CharacterEncoder;->pStream:Ljava/io/PrintStream;
-Lsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
-Lsun/misc/FloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FloatingDecimal;->getHexDigit(Ljava/lang/String;I)I
-Lsun/misc/FloatingDecimal;->stripLeadingZeros(Ljava/lang/String;)Ljava/lang/String;
-Lsun/misc/FormattedFloatingDecimal$Form;->COMPATIBLE:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->DECIMAL_FLOAT:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->SCIENTIFIC:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->rawCopySign(DD)D
-Lsun/misc/HexDumpEncoder;-><init>()V
-Lsun/misc/HexDumpEncoder;->currentByte:I
-Lsun/misc/HexDumpEncoder;->offset:I
-Lsun/misc/HexDumpEncoder;->thisLine:[B
-Lsun/misc/HexDumpEncoder;->thisLineLength:I
-Lsun/misc/IOUtils;->readFully(Ljava/io/InputStream;IZ)[B
-Lsun/misc/JarIndex;-><init>([Ljava/lang/String;)V
-Lsun/misc/JarIndex;->write(Ljava/io/OutputStream;)V
-Lsun/misc/MessageUtils;-><init>()V
-Lsun/misc/MetaIndex;->forJar(Ljava/io/File;)Lsun/misc/MetaIndex;
-Lsun/misc/MetaIndex;->registerDirectory(Ljava/io/File;)V
-Lsun/misc/VM;->maxDirectMemory()J
-Lsun/net/ftp/FtpClient;-><init>()V
-Lsun/net/util/IPAddressUtil;->isIPv4LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/util/IPAddressUtil;->isIPv6LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/www/MessageHeader;-><init>()V
-Lsun/net/www/MessageHeader;-><init>(Ljava/io/InputStream;)V
-Lsun/net/www/MessageHeader;->add(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->findValue(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/MessageHeader;->prepend(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->print(Ljava/io/PrintStream;)V
-Lsun/net/www/MessageHeader;->set(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/ParseUtil;->decode(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->encodePath(Ljava/lang/String;Z)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->fileToEncodedURL(Ljava/io/File;)Ljava/net/URL;
-Lsun/net/www/URLConnection;-><init>(Ljava/net/URL;)V
-Lsun/net/www/URLConnection;->setProperties(Lsun/net/www/MessageHeader;)V
-Lsun/nio/ch/DirectBuffer;->address()J
-Lsun/nio/ch/FileChannelImpl;->unmap0(JJ)I
-Lsun/nio/ch/SelectorImpl;->publicSelectedKeys:Ljava/util/Set;
-Lsun/nio/ch/SelectorImpl;->selectedKeys:Ljava/util/Set;
-Lsun/nio/cs/HistoricallyNamedCharset;->historicalName()Ljava/lang/String;
-Lsun/nio/cs/ThreadLocalCoders;->decoderFor(Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder;
-Lsun/nio/fs/BasicFileAttributesHolder;->get()Ljava/nio/file/attribute/BasicFileAttributes;
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/Class;)V
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/String;)V
-Lsun/reflect/misc/ReflectUtil;->isPackageAccessible(Ljava/lang/Class;)Z
-Lsun/reflect/misc/ReflectUtil;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/reflect/Reflection;->ensureMemberAccess(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V
-Lsun/reflect/Reflection;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/security/action/GetBooleanAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetIntegerAction;-><init>(Ljava/lang/String;I)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/jca/GetInstance$Instance;->impl:Ljava/lang/Object;
-Lsun/security/jca/GetInstance$Instance;->provider:Ljava/security/Provider;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/security/Provider;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/JCAUtil;->getSecureRandom()Ljava/security/SecureRandom;
-Lsun/security/jca/ProviderConfig;->argument:Ljava/lang/String;
-Lsun/security/jca/ProviderConfig;->CL_STRING:[Ljava/lang/Class;
-Lsun/security/jca/ProviderConfig;->disableLoad()V
-Lsun/security/jca/ProviderConfig;->hasArgument()Z
-Lsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service;
-Lsun/security/jca/Providers;->getProviderList()Lsun/security/jca/ProviderList;
-Lsun/security/jca/Providers;->startJarVerification()Ljava/lang/Object;
-Lsun/security/jca/Providers;->stopJarVerification(Ljava/lang/Object;)V
-Lsun/security/pkcs/ContentInfo;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/ContentInfo;-><init>([B)V
-Lsun/security/pkcs/ContentInfo;->DATA_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/ContentInfo;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/pkcs/ContentInfo;->getData()[B
-Lsun/security/pkcs/ParsingException;-><init>(Ljava/lang/String;)V
-Lsun/security/pkcs/PKCS7;-><init>([B)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Ljava/security/cert/X509CRL;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;->encodeSignedData(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS7;->getCertificates()[Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/PKCS7;->getContentInfo()Lsun/security/pkcs/ContentInfo;
-Lsun/security/pkcs/PKCS7;->getSignerInfos()[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify(Lsun/security/pkcs/SignerInfo;[B)Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify([B)[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS8Key;-><init>()V
-Lsun/security/pkcs/PKCS8Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/PKCS8Key;->encodedKey:[B
-Lsun/security/pkcs/PKCS8Key;->key:[B
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;->CONTENT_TYPE_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attribute;->EMAIL_ADDRESS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getValue()Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attribute;->MESSAGE_DIGEST_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->SIGNING_TIME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;Z)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>([Lsun/security/pkcs/PKCS9Attribute;)V
-Lsun/security/pkcs/PKCS9Attributes;->encode(BLjava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;->getAttribute(Ljava/lang/String;)Lsun/security/pkcs/PKCS9Attribute;
-Lsun/security/pkcs/PKCS9Attributes;->getAttributeValue(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attributes;->getDerEncoding()[B
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/PKCS9Attributes;Lsun/security/x509/AlgorithmId;[BLsun/security/pkcs/PKCS9Attributes;)V
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/x509/AlgorithmId;[B)V
-Lsun/security/pkcs/SignerInfo;->getCertificate(Lsun/security/pkcs/PKCS7;)Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/SignerInfo;->getCertificateChain(Lsun/security/pkcs/PKCS7;)Ljava/util/ArrayList;
-Lsun/security/pkcs/SignerInfo;->getDigestAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getDigestEncryptionAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getEncryptedDigest()[B
-Lsun/security/provider/certpath/X509CertificatePair;->clearCache()V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/util/List;)V
-Lsun/security/provider/certpath/X509CertPath;->certs:Ljava/util/List;
-Lsun/security/provider/certpath/X509CertPath;->getEncodingsStatic()Ljava/util/Iterator;
-Lsun/security/provider/X509Factory;->addToCache(Lsun/security/util/Cache;[BLjava/lang/Object;)V
-Lsun/security/provider/X509Factory;->certCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->crlCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->getFromCache(Lsun/security/util/Cache;[B)Ljava/lang/Object;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509Certificate;)Lsun/security/x509/X509CertImpl;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509CRL;)Lsun/security/x509/X509CRLImpl;
-Lsun/security/timestamp/TimestampToken;-><init>([B)V
-Lsun/security/timestamp/TimestampToken;->getDate()Ljava/util/Date;
-Lsun/security/timestamp/TimestampToken;->getHashAlgorithm()Lsun/security/x509/AlgorithmId;
-Lsun/security/timestamp/TimestampToken;->getHashedMessage()[B
-Lsun/security/timestamp/TimestampToken;->getNonce()Ljava/math/BigInteger;
-Lsun/security/util/BitArray;-><init>(I[B)V
-Lsun/security/util/BitArray;->toByteArray()[B
-Lsun/security/util/Cache;-><init>()V
-Lsun/security/util/Cache;->clear()V
-Lsun/security/util/Cache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lsun/security/util/Cache;->newHardMemoryCache(I)Lsun/security/util/Cache;
-Lsun/security/util/Cache;->put(Ljava/lang/Object;Ljava/lang/Object;)V
-Lsun/security/util/Debug;->getInstance(Ljava/lang/String;)Lsun/security/util/Debug;
-Lsun/security/util/Debug;->println()V
-Lsun/security/util/Debug;->println(Ljava/lang/String;)V
-Lsun/security/util/Debug;->toHexString(Ljava/math/BigInteger;)Ljava/lang/String;
-Lsun/security/util/DerIndefLenConverter;-><init>()V
-Lsun/security/util/DerIndefLenConverter;->convert([B)[B
-Lsun/security/util/DerIndefLenConverter;->data:[B
-Lsun/security/util/DerIndefLenConverter;->dataPos:I
-Lsun/security/util/DerIndefLenConverter;->dataSize:I
-Lsun/security/util/DerIndefLenConverter;->isIndefinite(I)Z
-Lsun/security/util/DerIndefLenConverter;->newData:[B
-Lsun/security/util/DerIndefLenConverter;->numOfTotalLenBytes:I
-Lsun/security/util/DerIndefLenConverter;->parseLength()I
-Lsun/security/util/DerIndefLenConverter;->parseTag()V
-Lsun/security/util/DerIndefLenConverter;->parseValue(I)V
-Lsun/security/util/DerIndefLenConverter;->writeLengthAndValue()V
-Lsun/security/util/DerIndefLenConverter;->writeTag()V
-Lsun/security/util/DerInputStream;-><init>([B)V
-Lsun/security/util/DerInputStream;->available()I
-Lsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerInputStream;->getBitString()[B
-Lsun/security/util/DerInputStream;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getInteger()I
-Lsun/security/util/DerInputStream;->getOctetString()[B
-Lsun/security/util/DerInputStream;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerInputStream;->getSequence(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(IZ)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date;
-Lsun/security/util/DerInputStream;->getUTF8String()Ljava/lang/String;
-Lsun/security/util/DerInputStream;->mark(I)V
-Lsun/security/util/DerInputStream;->peekByte()I
-Lsun/security/util/DerInputStream;->reset()V
-Lsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream;
-Lsun/security/util/DerInputStream;->tag:B
-Lsun/security/util/DerOutputStream;-><init>()V
-Lsun/security/util/DerOutputStream;-><init>(I)V
-Lsun/security/util/DerOutputStream;->putBitString([B)V
-Lsun/security/util/DerOutputStream;->putBoolean(Z)V
-Lsun/security/util/DerOutputStream;->putDerValue(Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putIA5String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putInteger(I)V
-Lsun/security/util/DerOutputStream;->putInteger(Ljava/math/BigInteger;)V
-Lsun/security/util/DerOutputStream;->putNull()V
-Lsun/security/util/DerOutputStream;->putOctetString([B)V
-Lsun/security/util/DerOutputStream;->putOID(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/util/DerOutputStream;->putOrderedSetOf(B[Lsun/security/util/DerEncoder;)V
-Lsun/security/util/DerOutputStream;->putPrintableString(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putSequence([Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putUTCTime(Ljava/util/Date;)V
-Lsun/security/util/DerOutputStream;->putUTF8String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->write(BLsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerOutputStream;->write(B[B)V
-Lsun/security/util/DerValue;-><init>(B[B)V
-Lsun/security/util/DerValue;-><init>(Ljava/io/InputStream;)V
-Lsun/security/util/DerValue;-><init>(Ljava/lang/String;)V
-Lsun/security/util/DerValue;-><init>([B)V
-Lsun/security/util/DerValue;-><init>([BII)V
-Lsun/security/util/DerValue;->buffer:Lsun/security/util/DerInputBuffer;
-Lsun/security/util/DerValue;->createTag(BZB)B
-Lsun/security/util/DerValue;->data:Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerValue;->getAsString()Ljava/lang/String;
-Lsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getBitString()[B
-Lsun/security/util/DerValue;->getData()Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->getDataBytes()[B
-Lsun/security/util/DerValue;->getOctetString()[B
-Lsun/security/util/DerValue;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerValue;->getPositiveBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray;
-Lsun/security/util/DerValue;->isConstructed()Z
-Lsun/security/util/DerValue;->isContextSpecific()Z
-Lsun/security/util/DerValue;->isContextSpecific(B)Z
-Lsun/security/util/DerValue;->isPrintableStringChar(C)Z
-Lsun/security/util/DerValue;->resetTag(B)V
-Lsun/security/util/DerValue;->tag:B
-Lsun/security/util/DerValue;->toByteArray()[B
-Lsun/security/util/DerValue;->toDerInputStream()Lsun/security/util/DerInputStream;
-Lsun/security/util/ManifestDigester$Entry;->digest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester$Entry;->digestWorkaround(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester;-><init>([B)V
-Lsun/security/util/ManifestDigester;->get(Ljava/lang/String;Z)Lsun/security/util/ManifestDigester$Entry;
-Lsun/security/util/ManifestDigester;->manifestDigest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/MemoryCache$HardCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;J)V
-Lsun/security/util/MemoryCache$SoftCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)V
-Lsun/security/util/ObjectIdentifier;-><init>(Ljava/lang/String;)V
-Lsun/security/util/ObjectIdentifier;-><init>([I)V
-Lsun/security/util/ObjectIdentifier;->equals(Lsun/security/util/ObjectIdentifier;)Z
-Lsun/security/util/ObjectIdentifier;->newInternal([I)Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/PropertyExpander;->expand(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/ResourcesMgr;->getString(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/SecurityConstants;->CREATE_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREADGROUP_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREAD_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SignatureFileVerifier;->isBlockOrSF(Ljava/lang/String;)Z
-Lsun/security/x509/AccessDescription;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/AccessDescription;->getAccessLocation()Lsun/security/x509/GeneralName;
-Lsun/security/x509/AccessDescription;->getAccessMethod()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;-><init>()V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/security/AlgorithmParameters;)V
-Lsun/security/x509/AlgorithmId;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/x509/AlgorithmId;->DSA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->EC_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->encode()[B
-Lsun/security/x509/AlgorithmId;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/AlgorithmId;->equals(Lsun/security/x509/AlgorithmId;)Z
-Lsun/security/x509/AlgorithmId;->getAlgorithmId(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->getDigAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncodedParams()[B
-Lsun/security/x509/AlgorithmId;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->getParameters()Ljava/security/AlgorithmParameters;
-Lsun/security/x509/AlgorithmId;->MD2_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->MD5_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->params:Lsun/security/util/DerValue;
-Lsun/security/x509/AlgorithmId;->parse(Lsun/security/util/DerValue;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->RSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->sha1WithRSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA256_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA384_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA512_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AttributeNameEnumeration;-><init>()V
-Lsun/security/x509/AVA;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/x509/AVA;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/x509/AVA;->getObjectIdentifier()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVA;->getValueString()Ljava/lang/String;
-Lsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;
-Lsun/security/x509/AVAComparator;->INSTANCE:Ljava/util/Comparator;
-Lsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->isCompliant(I)Z
-Lsun/security/x509/AVAKeyword;->keyword:Ljava/lang/String;
-Lsun/security/x509/AVAKeyword;->keywordMap:Ljava/util/Map;
-Lsun/security/x509/AVAKeyword;->oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->oidMap:Ljava/util/Map;
-Lsun/security/x509/CertificateAlgorithmId;-><init>(Lsun/security/x509/AlgorithmId;)V
-Lsun/security/x509/CertificateExtensions;-><init>()V
-Lsun/security/x509/CertificateExtensions;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/CertificateExtensions;->encode(Ljava/io/OutputStream;Z)V
-Lsun/security/x509/CertificateExtensions;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateExtensions;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/CertificateIssuerName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(I)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(Ljava/math/BigInteger;)V
-Lsun/security/x509/CertificateSubjectName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSubjectName;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateValidity;-><init>(Ljava/util/Date;Ljava/util/Date;)V
-Lsun/security/x509/CertificateVersion;-><init>(I)V
-Lsun/security/x509/CertificateX509Key;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/CRLDistributionPointsExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;-><init>(Ljava/lang/Boolean;Ljava/lang/Object;)V
-Lsun/security/x509/CRLNumberExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/Extension;-><init>(Lsun/security/x509/Extension;)V
-Lsun/security/x509/Extension;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/Extension;->getExtensionId()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/GeneralName;-><init>(Lsun/security/x509/GeneralNameInterface;)V
-Lsun/security/x509/GeneralName;->getName()Lsun/security/x509/GeneralNameInterface;
-Lsun/security/x509/GeneralName;->getType()I
-Lsun/security/x509/GeneralNames;-><init>()V
-Lsun/security/x509/GeneralNames;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/GeneralNames;->add(Lsun/security/x509/GeneralName;)Lsun/security/x509/GeneralNames;
-Lsun/security/x509/GeneralNames;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/GeneralNames;->isEmpty()Z
-Lsun/security/x509/KeyIdentifier;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/KeyIdentifier;->getIdentifier()[B
-Lsun/security/x509/KeyIdentifier;->octetString:[B
-Lsun/security/x509/KeyUsageExtension;-><init>([Z)V
-Lsun/security/x509/KeyUsageExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/NetscapeCertTypeExtension;-><init>([B)V
-Lsun/security/x509/NetscapeCertTypeExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/OIDMap$OIDInfo;->clazz:Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->getClass(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->nameMap:Ljava/util/Map;
-Lsun/security/x509/OIDMap;->oidMap:Ljava/util/Map;
-Lsun/security/x509/PKIXExtensions;->CertificateIssuer_Id:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/SerialNumber;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/SubjectAlternativeNameExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/SubjectKeyIdentifierExtension;-><init>([B)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/UniqueIdentity;->encode(Lsun/security/util/DerOutputStream;B)V
-Lsun/security/x509/URIName;->getName()Ljava/lang/String;
-Lsun/security/x509/URIName;->getScheme()Ljava/lang/String;
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X500Name;-><init>([B)V
-Lsun/security/x509/X500Name;->allAvas()Ljava/util/List;
-Lsun/security/x509/X500Name;->asX500Name(Ljavax/security/auth/x500/X500Principal;)Lsun/security/x509/X500Name;
-Lsun/security/x509/X500Name;->asX500Principal()Ljavax/security/auth/x500/X500Principal;
-Lsun/security/x509/X500Name;->commonName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->countryName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DNQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DOMAIN_COMPONENT_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/X500Name;->GENERATIONQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->getCommonName()Ljava/lang/String;
-Lsun/security/x509/X500Name;->GIVENNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->INITIALS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->ipAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->isEmpty()Z
-Lsun/security/x509/X500Name;->localityName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgUnitName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SERIALNUMBER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->stateName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->streetAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SURNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->title_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->userid_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/x509/X509CertInfo;)V
-Lsun/security/x509/X509CertImpl;-><init>([B)V
-Lsun/security/x509/X509CertImpl;->algId:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509CertImpl;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509CertImpl;->parse(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;->readOnly:Z
-Lsun/security/x509/X509CertImpl;->sign(Ljava/security/PrivateKey;Ljava/lang/String;)V
-Lsun/security/x509/X509CertImpl;->signature:[B
-Lsun/security/x509/X509CertImpl;->signedCert:[B
-Lsun/security/x509/X509CertInfo;-><init>()V
-Lsun/security/x509/X509CertInfo;-><init>([B)V
-Lsun/security/x509/X509CertInfo;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertInfo;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/X509CRLEntryImpl;->getExtension(Lsun/security/util/ObjectIdentifier;)Lsun/security/x509/Extension;
-Lsun/security/x509/X509CRLImpl;-><init>(Ljava/io/InputStream;)V
-Lsun/security/x509/X509CRLImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CRLImpl;-><init>([B)V
-Lsun/security/x509/X509CRLImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509Key;-><init>()V
-Lsun/security/x509/X509Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509Key;->encodedKey:[B
-Lsun/security/x509/X509Key;->key:[B
-Lsun/security/x509/X509Key;->parse(Lsun/security/util/DerValue;)Ljava/security/PublicKey;
-Lsun/security/x509/X509Key;->unusedBits:I
-Lsun/util/calendar/AbstractCalendar;->getDayOfWeekDateOnOrBefore(JI)J
-Lsun/util/calendar/AbstractCalendar;->getTimeOfDayValue(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/BaseCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/BaseCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/CalendarDate;->getDayOfMonth()I
-Lsun/util/calendar/CalendarDate;->getMonth()I
-Lsun/util/calendar/CalendarDate;->getTimeOfDay()J
-Lsun/util/calendar/CalendarDate;->getYear()I
-Lsun/util/calendar/CalendarDate;->setDate(III)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setDayOfMonth(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setHours(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMillis(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMinutes(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setSeconds(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->forName(Ljava/lang/String;)Lsun/util/calendar/CalendarSystem;
-Lsun/util/calendar/CalendarSystem;->getGregorianCalendar()Lsun/util/calendar/Gregorian;
-Lsun/util/calendar/CalendarSystem;->getTime(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/CalendarSystem;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->validate(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/CalendarUtils;->floorDivide(II)I
-Lsun/util/calendar/CalendarUtils;->floorDivide(JJ)J
-Lsun/util/calendar/CalendarUtils;->mod(II)I
-Lsun/util/calendar/CalendarUtils;->mod(JJ)J
-Lsun/util/calendar/Era;-><init>(Ljava/lang/String;Ljava/lang/String;JZ)V
-Lsun/util/calendar/Era;->getAbbreviation()Ljava/lang/String;
-Lsun/util/calendar/Era;->getName()Ljava/lang/String;
-Lsun/util/calendar/Era;->getSinceDate()Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/ImmutableGregorianDate;->unsupported()V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setEra(Lsun/util/calendar/Era;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setYear(I)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->normalize(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/LocalGregorianCalendar;->validate(Lsun/util/calendar/CalendarDate;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
deleted file mode 100644
index 575ba34..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;Z)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)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/config/preloaded-classes b/config/preloaded-classes
index 550e795..3095925 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4117,6 +4117,10 @@
com.android.internal.util.VirtualRefBasePtr
com.android.internal.util.XmlUtils
com.android.internal.util.XmlUtils$WriteMapCallback
+com.android.internal.util.function.NonaConsumer
+com.android.internal.util.function.NonaFunction
+com.android.internal.util.function.OctConsumer
+com.android.internal.util.function.OctFunction
com.android.internal.util.function.HeptConsumer
com.android.internal.util.function.HeptFunction
com.android.internal.util.function.HexConsumer
@@ -6172,6 +6176,9 @@
libcore.reflect.Types
libcore.reflect.WildcardTypeImpl
libcore.timezone.TimeZoneDataFiles
+libcore.timezone.ZoneInfoDB
+libcore.timezone.ZoneInfoDB$TzData
+libcore.timezone.ZoneInfoDB$TzData$1
libcore.util.BasicLruCache
libcore.util.CharsetUtils
libcore.util.CollectionUtils
@@ -6184,9 +6191,6 @@
libcore.util.ZoneInfo
libcore.util.ZoneInfo$CheckedArithmeticException
libcore.util.ZoneInfo$WallTime
-libcore.util.ZoneInfoDB
-libcore.util.ZoneInfoDB$TzData
-libcore.util.ZoneInfoDB$TzData$1
org.apache.harmony.dalvik.NativeTestTarget
org.apache.harmony.dalvik.ddmc.Chunk
org.apache.harmony.dalvik.ddmc.ChunkHandler
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 83fab7e..cfda803 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.IntelligenceManager;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -824,9 +824,11 @@
/** The autofill manager. Always access via {@link #getAutofillManager()}. */
@Nullable private AutofillManager mAutofillManager;
- /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
- @Nullable private IntelligenceManager mIntelligenceManager;
+ /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */
+ @Nullable private ContentCaptureManager mContentCaptureManager;
+ private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+ new ArrayList<Application.ActivityLifecycleCallbacks>();
static final class NonConfigurationInstances {
Object activity;
@@ -1014,39 +1016,39 @@
}
/**
- * (Creates, sets, and ) returns the intelligence manager
+ * (Creates, sets, and ) returns the content capture manager
*
- * @return The intelligence manager
+ * @return The content capture manager
*/
- @NonNull private IntelligenceManager getIntelligenceManager() {
- if (mIntelligenceManager == null) {
- mIntelligenceManager = getSystemService(IntelligenceManager.class);
+ @NonNull private ContentCaptureManager getContentCaptureManager() {
+ if (mContentCaptureManager == null) {
+ mContentCaptureManager = getSystemService(ContentCaptureManager.class);
}
- return mIntelligenceManager;
+ return mContentCaptureManager;
}
- private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
- final IntelligenceManager im = getIntelligenceManager();
- if (im == null || !im.isContentCaptureEnabled()) {
+ private void notifyContentCaptureManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+ final ContentCaptureManager cm = getContentCaptureManager();
+ if (cm == null || !cm.isContentCaptureEnabled()) {
return;
}
switch (event) {
case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
//TODO(b/111276913): decide whether the InteractionSessionId should be
// saved / restored in the activity bundle.
- im.onActivityCreated(mToken, getComponentName());
+ cm.onActivityCreated(mToken, getComponentName());
break;
case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
- im.onActivityDestroyed();
+ cm.onActivityDestroyed();
break;
case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
- im.onActivityLifecycleEvent(event);
+ cm.onActivityLifecycleEvent(event);
break;
default:
- Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
+ Log.w(TAG, "notifyContentCaptureManagerIfNeeded(): invalid type " + event);
}
}
@@ -1055,6 +1057,7 @@
super.attachBaseContext(newBase);
if (newBase != null) {
newBase.setAutofillClient(this);
+ newBase.setContentCaptureSupported(true);
}
}
@@ -1064,6 +1067,294 @@
return this;
}
+ /** @hide */
+ @Override
+ public boolean isContentCaptureSupported() {
+ return true;
+ }
+
+ /**
+ * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives
+ * lifecycle callbacks for only this Activity.
+ * <p>
+ * In relation to any
+ * {@link Application#registerActivityLifecycleCallbacks Application registered callbacks},
+ * the callbacks registered here will always occur nested within those callbacks. This means:
+ * <ul>
+ * <li>Pre events will first be sent to Application registered callbacks, then to callbacks
+ * registered here.</li>
+ * <li>{@link Application.ActivityLifecycleCallbacks#onActivityCreated(Activity, Bundle)},
+ * {@link Application.ActivityLifecycleCallbacks#onActivityStarted(Activity)}, and
+ * {@link Application.ActivityLifecycleCallbacks#onActivityResumed(Activity)} will
+ * be sent first to Application registered callbacks, then to callbacks registered here.
+ * For all other events, callbacks registered here will be sent first.</li>
+ * <li>Post events will first be sent to callbacks registered here, then to
+ * Application registered callbacks.</li>
+ * </ul>
+ * <p>
+ * If multiple callbacks are registered here, they receive events in a first in (up through
+ * {@link Application.ActivityLifecycleCallbacks#onActivityPostResumed}, last out
+ * ordering.
+ * <p>
+ * It is strongly recommended to register this in the constructor of your Activity to ensure
+ * you get all available callbacks. As this callback is associated with only this Activity,
+ * it is not usually necessary to {@link #unregisterActivityLifecycleCallbacks unregister} it
+ * unless you specifically do not want to receive further lifecycle callbacks.
+ *
+ * @param callback The callback instance to register
+ */
+ public void registerActivityLifecycleCallbacks(
+ @NonNull Application.ActivityLifecycleCallbacks callback) {
+ synchronized (mActivityLifecycleCallbacks) {
+ mActivityLifecycleCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Unregister an {@link Application.ActivityLifecycleCallbacks} previously registered
+ * with {@link #registerActivityLifecycleCallbacks}. It will not receive any further
+ * callbacks.
+ *
+ * @param callback The callback instance to unregister
+ * @see #registerActivityLifecycleCallbacks
+ */
+ public void unregisterActivityLifecycleCallbacks(
+ @NonNull Application.ActivityLifecycleCallbacks callback) {
+ synchronized (mActivityLifecycleCallbacks) {
+ mActivityLifecycleCallbacks.remove(callback);
+ }
+ }
+
+ private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) {
+ getApplication().dispatchActivityPreCreated(this, savedInstanceState);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(this,
+ savedInstanceState);
+ }
+ }
+ }
+
+ private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) {
+ getApplication().dispatchActivityCreated(this, savedInstanceState);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
+ savedInstanceState);
+ }
+ }
+ }
+
+ private void dispatchActivityPostCreated(@Nullable Bundle savedInstanceState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(this,
+ savedInstanceState);
+ }
+ }
+ getApplication().dispatchActivityPostCreated(this, savedInstanceState);
+ }
+
+ private void dispatchActivityPreStarted() {
+ getApplication().dispatchActivityPreStarted(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(this);
+ }
+ }
+ }
+
+ private void dispatchActivityStarted() {
+ getApplication().dispatchActivityStarted(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this);
+ }
+ }
+ }
+
+ private void dispatchActivityPostStarted() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostStarted(this);
+ }
+ }
+ getApplication().dispatchActivityPostStarted(this);
+ }
+
+ private void dispatchActivityPreResumed() {
+ getApplication().dispatchActivityPreResumed(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(this);
+ }
+ }
+ }
+
+ private void dispatchActivityResumed() {
+ getApplication().dispatchActivityResumed(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(this);
+ }
+ }
+ }
+
+ private void dispatchActivityPostResumed() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(this);
+ }
+ }
+ getApplication().dispatchActivityPostResumed(this);
+ }
+
+ private void dispatchActivityPrePaused() {
+ getApplication().dispatchActivityPrePaused(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(this);
+ }
+ }
+ }
+
+ private void dispatchActivityPaused() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(this);
+ }
+ }
+ getApplication().dispatchActivityPaused(this);
+ }
+
+ private void dispatchActivityPostPaused() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(this);
+ }
+ }
+ getApplication().dispatchActivityPostPaused(this);
+ }
+
+ private void dispatchActivityPreStopped() {
+ getApplication().dispatchActivityPreStopped(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(this);
+ }
+ }
+ }
+
+ private void dispatchActivityStopped() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(this);
+ }
+ }
+ getApplication().dispatchActivityStopped(this);
+ }
+
+ private void dispatchActivityPostStopped() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostStopped(this);
+ }
+ }
+ getApplication().dispatchActivityPostStopped(this);
+ }
+
+ private void dispatchActivityPreSaveInstanceState(@NonNull Bundle outState) {
+ getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPreSaveInstanceState(this, outState);
+ }
+ }
+ }
+
+ private void dispatchActivitySaveInstanceState(@NonNull Bundle outState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivitySaveInstanceState(this, outState);
+ }
+ }
+ getApplication().dispatchActivitySaveInstanceState(this, outState);
+ }
+
+ private void dispatchActivityPostSaveInstanceState(@NonNull Bundle outState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostSaveInstanceState(this, outState);
+ }
+ }
+ getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+ }
+
+ private void dispatchActivityPreDestroyed() {
+ getApplication().dispatchActivityPreDestroyed(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPreDestroyed(this);
+ }
+ }
+ }
+
+ private void dispatchActivityDestroyed() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this);
+ }
+ }
+ getApplication().dispatchActivityDestroyed(this);
+ }
+
+ private void dispatchActivityPostDestroyed() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostDestroyed(this);
+ }
+ }
+ getApplication().dispatchActivityPostDestroyed(this);
+ }
+
+ private Object[] collectActivityLifecycleCallbacks() {
+ Object[] callbacks = null;
+ synchronized (mActivityLifecycleCallbacks) {
+ if (mActivityLifecycleCallbacks.size() > 0) {
+ callbacks = mActivityLifecycleCallbacks.toArray();
+ }
+ }
+ return callbacks;
+ }
+
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
@@ -1119,14 +1410,14 @@
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
- getApplication().dispatchActivityCreated(this, savedInstanceState);
+ dispatchActivityCreated(savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
}
/**
@@ -1355,12 +1646,12 @@
mFragments.doLoaderStart();
- getApplication().dispatchActivityStarted(this);
+ dispatchActivityStarted();
if (mAutoFillResetNeeded) {
getAutofillManager().onVisibleForAutofill();
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
}
/**
@@ -1426,7 +1717,7 @@
@CallSuper
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
- getApplication().dispatchActivityResumed(this);
+ dispatchActivityResumed();
mActivityTransitionState.onResume(this, isTopOfTask());
enableAutofillCompatibilityIfNeeded();
if (mAutoFillResetNeeded) {
@@ -1451,7 +1742,7 @@
}
}
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
mCalled = true;
}
@@ -1642,13 +1933,13 @@
* @param outState The bundle to save the state to.
*/
final void performSaveInstanceState(@NonNull Bundle outState) {
- getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+ dispatchActivityPreSaveInstanceState(outState);
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
- getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+ dispatchActivityPostSaveInstanceState(outState);
}
/**
@@ -1662,13 +1953,13 @@
*/
final void performSaveInstanceState(@NonNull Bundle outState,
@NonNull PersistableBundle outPersistentState) {
- getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+ dispatchActivityPreSaveInstanceState(outState);
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
", " + outPersistentState);
- getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+ dispatchActivityPostSaveInstanceState(outState);
}
/**
@@ -1731,7 +2022,7 @@
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
- getApplication().dispatchActivitySaveInstanceState(this, outState);
+ dispatchActivitySaveInstanceState(outState);
}
/**
@@ -1831,7 +2122,7 @@
@CallSuper
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
- getApplication().dispatchActivityPaused(this);
+ dispatchActivityPaused();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
@@ -1845,7 +2136,7 @@
mAutoFillIgnoreFirstResumePause = false;
}
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
mCalled = true;
}
@@ -2015,7 +2306,7 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
mActivityTransitionState.onStop();
- getApplication().dispatchActivityStopped(this);
+ dispatchActivityStopped();
mTranslucentCallback = null;
mCalled = true;
@@ -2034,7 +2325,7 @@
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
}
}
@@ -2104,9 +2395,9 @@
mActionBar.onDestroy();
}
- getApplication().dispatchActivityDestroyed(this);
+ dispatchActivityDestroyed();
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
}
@@ -6522,7 +6813,7 @@
}
void dumpIntelligenceManager(String prefix, PrintWriter writer) {
- final IntelligenceManager im = getIntelligenceManager();
+ final ContentCaptureManager im = getContentCaptureManager();
if (im != null) {
im.dump(prefix, writer);
} else {
@@ -7284,7 +7575,7 @@
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
- getApplication().dispatchActivityPreCreated(this, icicle);
+ dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
@@ -7299,7 +7590,7 @@
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
- getApplication().dispatchActivityPostCreated(this, icicle);
+ dispatchActivityPostCreated(icicle);
}
final void performNewIntent(@NonNull Intent intent) {
@@ -7308,7 +7599,7 @@
}
final void performStart(String reason) {
- getApplication().dispatchActivityPreStarted(this);
+ dispatchActivityPreStarted();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
@@ -7351,7 +7642,7 @@
}
mActivityTransitionState.enterReady(this);
- getApplication().dispatchActivityPostStarted(this);
+ dispatchActivityPostStarted();
}
/**
@@ -7406,7 +7697,7 @@
}
final void performResume(boolean followedByPause, String reason) {
- getApplication().dispatchActivityPreResumed(this);
+ dispatchActivityPreResumed();
performRestart(true /* start */, reason);
mFragments.execPendingActions();
@@ -7456,11 +7747,11 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPostResume()");
}
- getApplication().dispatchActivityPostResumed(this);
+ dispatchActivityPostResumed();
}
final void performPause() {
- getApplication().dispatchActivityPrePaused(this);
+ dispatchActivityPrePaused();
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
@@ -7473,7 +7764,7 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
- getApplication().dispatchActivityPostPaused(this);
+ dispatchActivityPostPaused();
}
final void performUserLeaving() {
@@ -7489,7 +7780,7 @@
mCanEnterPictureInPicture = false;
if (!mStopped) {
- getApplication().dispatchActivityPreStopped(this);
+ dispatchActivityPreStopped();
if (mWindow != null) {
mWindow.closeAllPanels();
}
@@ -7524,13 +7815,13 @@
}
mStopped = true;
- getApplication().dispatchActivityPostStopped(this);
+ dispatchActivityPostStopped();
}
mResumed = false;
}
final void performDestroy() {
- getApplication().dispatchActivityPreDestroyed(this);
+ dispatchActivityPreDestroyed();
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
@@ -7540,7 +7831,7 @@
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
- getApplication().dispatchActivityPostDestroyed(this);
+ dispatchActivityPostDestroyed();
}
final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1edd7f5..af3da0c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -243,6 +243,8 @@
public abstract void ensureBootCompleted();
public abstract void updateOomLevelsForDisplay(int displayId);
public abstract boolean isActivityStartsLoggingEnabled();
+ /** Returns true if the background activity starts is enabled. */
+ public abstract boolean isBackgroundActivityStartsEnabled();
public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
/** Input dispatch timeout to a window, start the ANR process. */
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 56ccf6f..6fdf7c8 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -433,4 +433,18 @@
}
return sb.toString();
}
+
+ /**
+ * 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 {
+ getService().clearLaunchParamsForPackages(packageNames);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 805fb68..492aad9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -157,13 +157,11 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.OpenSSLSocketImpl;
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;
@@ -5324,16 +5322,6 @@
}
}
- /**
- * Updates the application info.
- *
- * This only works in the system process. Must be called on the main thread.
- */
- public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
- Preconditions.checkState(mSystemThread, "Must only be called in the system process");
- handleApplicationInfoChanged(ai);
- }
-
void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
// Updates triggered by package installation go through a package update
// receiver. Here we try to capture ApplicationInfo changes that are
@@ -5959,16 +5947,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 c879db8..2c435a2 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -31,10 +31,11 @@
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;
import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -59,12 +60,16 @@
private VirtualDisplay mVirtualDisplay;
private final SurfaceView mSurfaceView;
- private Surface mSurface;
+
+ /**
+ * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
+ * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
+ */
+ private SurfaceControl mRootSurfaceControl;
private final SurfaceCallback mSurfaceCallback;
private StateCallback mActivityViewCallback;
- private IActivityManager mActivityManager;
private IActivityTaskManager mActivityTaskManager;
private IInputForwarder mInputForwarder;
// Temp container to store view coordinates on screen.
@@ -75,6 +80,9 @@
private final CloseGuard mGuard = CloseGuard.get();
private boolean mOpened; // Protected by mGuard.
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+ private Surface mTmpSurface = new Surface();
+
@UnsupportedAppUsage
public ActivityView(Context context) {
this(context, null /* attrs */);
@@ -87,7 +95,6 @@
public ActivityView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mActivityManager = ActivityManager.getService();
mActivityTaskManager = ActivityTaskManager.getService();
mSurfaceView = new SurfaceView(context);
mSurfaceCallback = new SurfaceCallback();
@@ -283,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();
@@ -297,14 +309,19 @@
private class SurfaceCallback implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
- mSurface = mSurfaceView.getHolder().getSurface();
+ mTmpSurface = new Surface();
if (mVirtualDisplay == null) {
- initVirtualDisplay();
+ initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
if (mVirtualDisplay != null && mActivityViewCallback != null) {
mActivityViewCallback.onActivityViewReady(ActivityView.this);
}
} else {
- mVirtualDisplay.setSurface(surfaceHolder.getSurface());
+ // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+ // whether it has a surface. Setting a fake surface here so DisplayManager will
+ // consider this display on.
+ mVirtualDisplay.setSurface(mTmpSurface);
+ mTmpTransaction.reparent(mRootSurfaceControl,
+ mSurfaceView.getSurfaceControl().getHandle()).apply();
}
updateLocation();
}
@@ -319,8 +336,8 @@
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
- mSurface.release();
- mSurface = null;
+ mTmpSurface.release();
+ mTmpSurface = null;
if (mVirtualDisplay != null) {
mVirtualDisplay.setSurface(null);
}
@@ -328,7 +345,7 @@
}
}
- private void initVirtualDisplay() {
+ private void initVirtualDisplay(SurfaceSession surfaceSession) {
if (mVirtualDisplay != null) {
throw new IllegalStateException("Trying to initialize for the second time.");
}
@@ -336,9 +353,13 @@
final int width = mSurfaceView.getWidth();
final int height = mSurfaceView.getHeight();
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+ // whether it has a surface. Setting a fake surface here so DisplayManager will consider
+ // this display on.
mVirtualDisplay = displayManager.createVirtualDisplay(
DISPLAY_NAME + "@" + System.identityHashCode(this),
- width, height, getBaseDisplayDensity(), mSurface,
+ width, height, getBaseDisplayDensity(), mTmpSurface,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
| DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
if (mVirtualDisplay == null) {
@@ -348,11 +369,20 @@
final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+ mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ .setContainerLayer(true)
+ .setName(DISPLAY_NAME)
+ .build();
+
try {
+ wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
wm.dontOverrideDisplayInfo(displayId);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
+ mTmpTransaction.show(mRootSurfaceControl).apply();
mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
mTaskStackListener = new TaskStackListenerImpl();
try {
@@ -392,9 +422,9 @@
displayReleased = false;
}
- if (mSurface != null) {
- mSurface.release();
- mSurface = null;
+ if (mTmpSurface != null) {
+ mTmpSurface.release();
+ mTmpSurface = null;
}
if (displayReleased && mActivityViewCallback != null) {
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9b13420..2f0f14aa 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -35,7 +35,7 @@
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
import java.io.IOException;
import java.lang.annotation.Retention;
diff --git a/core/java/android/app/AppDetailsActivity.java b/core/java/android/app/AppDetailsActivity.java
index cd36e63..b71af88 100644
--- a/core/java/android/app/AppDetailsActivity.java
+++ b/core/java/android/app/AppDetailsActivity.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.TestApi;
import android.content.Intent;
import android.os.Bundle;
@@ -24,7 +25,9 @@
*
* @hide
*/
+@TestApi
public class AppDetailsActivity extends Activity {
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 63a41ec..3069be6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1180,11 +1180,11 @@
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.WRITE_MEDIA_AUDIO,
+ null, // no permission for OP_WRITE_MEDIA_AUDIO
Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.WRITE_MEDIA_VIDEO,
+ null, // no permission for OP_WRITE_MEDIA_VIDEO
Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.WRITE_MEDIA_IMAGES,
+ null, // no permission for OP_WRITE_MEDIA_IMAGES
};
/**
@@ -1462,11 +1462,11 @@
AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_AUDIO
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_VIDEO
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_IMAGES
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
};
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8bb704d..7312b2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2046,8 +2046,6 @@
StorageManager storage) {
if (app.isInternal()) {
return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
- } else if (app.isExternalAsec()) {
- return storage.getPrimaryPhysicalVolume();
} else {
return storage.findVolumeByUuid(app.volumeUuid);
}
@@ -2983,4 +2981,13 @@
throw e.rethrowAsRuntimeException();
}
}
+
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() {
+ try {
+ mPM.sendDeviceCustomizationReadyBroadcast();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 28ecb27..c7a9d99 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -217,6 +217,8 @@
private AutofillClient mAutofillClient = null;
private boolean mIsAutofillCompatEnabled;
+ private boolean mIsContentCaptureSupported = false;
+
private final Object mSync = new Object();
@GuardedBy("mSync")
@@ -1729,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) {
@@ -2376,6 +2381,18 @@
mIsAutofillCompatEnabled = autofillCompatEnabled;
}
+ /** @hide */
+ @Override
+ public boolean isContentCaptureSupported() {
+ return mIsContentCaptureSupported;
+ }
+
+ /** @hide */
+ @Override
+ public void setContentCaptureSupported(boolean supported) {
+ mIsContentCaptureSupported = supported;
+ }
+
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
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/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 09b77d5..777a494 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -445,4 +445,9 @@
void setPackageScreenCompatMode(in String packageName, int mode);
boolean getPackageAskScreenCompat(in String packageName);
void setPackageAskScreenCompat(in String packageName, boolean ask);
+
+ /**
+ * Clears launch params for given packages.
+ */
+ void clearLaunchParamsForPackages(in List<String> packageNames);
}
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/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5ef4be1..3a2038d 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -87,24 +87,24 @@
/**
* Sets the dimension hint for the wallpaper. These hints indicate the desired
- * minimum width and height for the wallpaper.
+ * minimum width and height for the wallpaper in a particular display.
*/
- void setDimensionHints(in int width, in int height, in String callingPackage);
+ void setDimensionHints(in int width, in int height, in String callingPackage, int displayId);
/**
- * Returns the desired minimum width for the wallpaper.
+ * Returns the desired minimum width for the wallpaper in a particular display.
*/
- int getWidthHint();
+ int getWidthHint(int displayId);
/**
- * Returns the desired minimum height for the wallpaper.
+ * Returns the desired minimum height for the wallpaper in a particular display.
*/
- int getHeightHint();
+ int getHeightHint(int displayId);
/**
* Sets extra padding that we would like the wallpaper to have outside of the display.
*/
- void setDisplayPadding(in Rect padding, in String callingPackage);
+ void setDisplayPadding(in Rect padding, in String callingPackage, int displayId);
/**
* Returns the name of the wallpaper. Private API.
@@ -159,5 +159,5 @@
/**
* Called from SystemUI when it shows the AoD UI.
*/
- oneway void setInAmbientMode(boolean inAmbientMode, boolean animated);
+ oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
}
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.aidl b/core/java/android/app/Notification.aidl
index 9d8129c..8a7156e 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,3 +17,4 @@
package android.app;
parcelable Notification;
+parcelable Notification.Action;
\ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f2a3e44..aa1b5af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1338,6 +1338,11 @@
private int mBadgeIcon = BADGE_ICON_NONE;
/**
+ * Determines whether the platform can generate contextual actions for a notification.
+ */
+ private boolean mAllowSystemGeneratedContextualActions = true;
+
+ /**
* Structure to encapsulate a named action that can be shown as part of this notification.
* It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
* selected by the user.
@@ -1688,11 +1693,30 @@
}
/**
+ * Throws an NPE if we are building a contextual action missing one of the fields
+ * necessary to display the action.
+ */
+ private void checkContextualActionNullFields() {
+ if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return;
+
+ if (mIcon == null) {
+ throw new NullPointerException("Contextual Actions must contain a valid icon");
+ }
+
+ if (mIntent == null) {
+ throw new NullPointerException(
+ "Contextual Actions must contain a valid PendingIntent");
+ }
+ }
+
+ /**
* Combine all of the options that have been set and return a new {@link Action}
* object.
* @return the built action
*/
public Action build() {
+ checkContextualActionNullFields();
+
ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
RemoteInput[] previousDataInputs =
(RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
@@ -2238,6 +2262,8 @@
if (parcel.readInt() != 0) {
mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
}
+
+ mAllowSystemGeneratedContextualActions = parcel.readBoolean();
}
@Override
@@ -2353,6 +2379,7 @@
that.mSettingsText = this.mSettingsText;
that.mGroupAlertBehavior = this.mGroupAlertBehavior;
that.mAppOverlayIntent = this.mAppOverlayIntent;
+ that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
if (!heavy) {
that.lightenPayload(); // will clean out extras
@@ -2681,6 +2708,8 @@
parcel.writeInt(0);
}
+ parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+
// mUsesStandardHeader is not written because it should be recomputed in listeners
}
@@ -3101,6 +3130,10 @@
return mAppOverlayIntent;
}
+ public boolean getAllowSystemGeneratedContextualActions() {
+ return mAllowSystemGeneratedContextualActions;
+ }
+
/**
* The small icon representing this notification in the status bar and content view.
*
@@ -3834,6 +3867,9 @@
* The system UI may choose to display a heads-up notification, instead of
* launching this intent, while the user is using the device.
* </p>
+ * <p>Apps targeting {@link Build.VERSION_CODES#Q} and above will have to request
+ * a permission ({@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}) in order to
+ * use full screen intents.</p>
*
* @param intent The pending intent to launch.
* @param highPriority Passing true will cause this notification to be sent
@@ -4422,19 +4458,27 @@
return bitmap;
}
- private void bindProfileBadge(RemoteViews contentView) {
+ private void bindProfileBadge(RemoteViews contentView, StandardTemplateParams p) {
Bitmap profileBadge = getProfileBadge();
if (profileBadge != null) {
contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
- if (isColorized()) {
+ if (isColorized(p)) {
contentView.setDrawableTint(R.id.profile_badge, false,
- getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
+ getPrimaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
}
}
}
+ private void bindAlertedIcon(RemoteViews contentView, StandardTemplateParams p) {
+ contentView.setDrawableTint(
+ R.id.alerted_icon,
+ false /* targetBackground */,
+ getNeutralColor(p),
+ PorterDuff.Mode.SRC_ATOP);
+ }
+
/**
* @hide
*/
@@ -4493,16 +4537,6 @@
result);
}
- /**
- * @param hasProgress whether the progress bar should be shown and set
- * @param result
- */
- private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
- TemplateBindResult result) {
- return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
- .fillTextsFrom(this), result);
- }
-
private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
TemplateBindResult result) {
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -4510,15 +4544,15 @@
resetStandardTemplate(contentView);
final Bundle ex = mN.extras;
- updateBackgroundColor(contentView);
- bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
+ updateBackgroundColor(contentView, p);
+ bindNotificationHeader(contentView, p);
bindLargeIconAndReply(contentView, p, result);
- boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
+ boolean showProgress = handleProgressBar(contentView, ex, p);
if (p.title != null) {
contentView.setViewVisibility(R.id.title, View.VISIBLE);
contentView.setTextViewText(R.id.title, processTextSpans(p.title));
if (!p.ambient) {
- setTextViewColorPrimary(contentView, R.id.title);
+ setTextViewColorPrimary(contentView, R.id.title, p);
}
contentView.setViewLayoutWidth(R.id.title, showProgress
? ViewGroup.LayoutParams.WRAP_CONTENT
@@ -4529,7 +4563,7 @@
: com.android.internal.R.id.text;
contentView.setTextViewText(textId, processTextSpans(p.text));
if (!p.ambient) {
- setTextViewColorSecondary(contentView, textId);
+ setTextViewColorSecondary(contentView, textId, p);
}
contentView.setViewVisibility(textId, View.VISIBLE);
}
@@ -4546,8 +4580,9 @@
return text;
}
- private void setTextViewColorPrimary(RemoteViews contentView, int id) {
- ensureColors();
+ private void setTextViewColorPrimary(RemoteViews contentView, int id,
+ StandardTemplateParams p) {
+ ensureColors(p);
contentView.setTextColor(id, mPrimaryTextColor);
}
@@ -4556,42 +4591,63 @@
}
/**
- * @return the primary text color
+ * Return the primary text color using the existing template params
* @hide
*/
@VisibleForTesting
public int getPrimaryTextColor() {
- ensureColors();
+ return getPrimaryTextColor(mParams);
+ }
+
+ /**
+ * @param p the template params to inflate this with
+ * @return the primary text color
+ * @hide
+ */
+ @VisibleForTesting
+ public int getPrimaryTextColor(StandardTemplateParams p) {
+ ensureColors(p);
return mPrimaryTextColor;
}
/**
- * @return the secondary text color
+ * Return the secondary text color using the existing template params
* @hide
*/
@VisibleForTesting
public int getSecondaryTextColor() {
- ensureColors();
+ return getSecondaryTextColor(mParams);
+ }
+
+ /**
+ * @param p the template params to inflate this with
+ * @return the secondary text color
+ * @hide
+ */
+ @VisibleForTesting
+ public int getSecondaryTextColor(StandardTemplateParams p) {
+ ensureColors(p);
return mSecondaryTextColor;
}
- private void setTextViewColorSecondary(RemoteViews contentView, int id) {
- ensureColors();
+ private void setTextViewColorSecondary(RemoteViews contentView, int id,
+ StandardTemplateParams p) {
+ ensureColors(p);
contentView.setTextColor(id, mSecondaryTextColor);
}
- private void ensureColors() {
- int backgroundColor = getBackgroundColor();
+ private void ensureColors(StandardTemplateParams p) {
+ int backgroundColor = getBackgroundColor(p);
if (mPrimaryTextColor == COLOR_INVALID
|| mSecondaryTextColor == COLOR_INVALID
|| mTextColorsAreForBackground != backgroundColor) {
mTextColorsAreForBackground = backgroundColor;
- if (!hasForegroundColor() || !isColorized()) {
+ if (!hasForegroundColor() || !isColorized(p)) {
mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
backgroundColor, mInNightMode);
mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
backgroundColor, mInNightMode);
- if (backgroundColor != COLOR_DEFAULT && isColorized()) {
+ if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
mPrimaryTextColor, backgroundColor, 4.5);
mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
@@ -4659,10 +4715,11 @@
}
}
- private void updateBackgroundColor(RemoteViews contentView) {
- if (isColorized()) {
+ private void updateBackgroundColor(RemoteViews contentView,
+ StandardTemplateParams p) {
+ if (isColorized(p)) {
contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
- getBackgroundColor());
+ getBackgroundColor(p));
} else {
// Clear it!
contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
@@ -4685,19 +4742,20 @@
remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
}
- private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
+ private boolean handleProgressBar(RemoteViews contentView, Bundle ex,
+ StandardTemplateParams p) {
final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
final int progress = ex.getInt(EXTRA_PROGRESS, 0);
final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
- if (hasProgress && (max != 0 || ind)) {
+ if (p.hasProgress && (max != 0 || ind)) {
contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
contentView.setProgressBar(
R.id.progress, max, progress, ind);
contentView.setProgressBackgroundTintList(
R.id.progress, ColorStateList.valueOf(mContext.getColor(
R.color.notification_progress_background_color)));
- if (mN.color != COLOR_DEFAULT) {
- ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
+ if (getRawColor(p) != COLOR_DEFAULT) {
+ ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor(p));
contentView.setProgressTintList(R.id.progress, colorStateList);
contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
}
@@ -4710,8 +4768,8 @@
private void bindLargeIconAndReply(RemoteViews contentView, StandardTemplateParams p,
TemplateBindResult result) {
- boolean largeIconShown = bindLargeIcon(contentView, p.hideLargeIcon || p.ambient);
- boolean replyIconShown = bindReplyIcon(contentView, p.hideReplyIcon || p.ambient);
+ boolean largeIconShown = bindLargeIcon(contentView, p);
+ boolean replyIconShown = bindReplyIcon(contentView, p);
contentView.setViewVisibility(R.id.right_icon_container,
largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
@@ -4759,15 +4817,15 @@
* Bind the large icon.
* @return if the largeIcon is visible
*/
- private boolean bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon) {
+ private boolean bindLargeIcon(RemoteViews contentView, StandardTemplateParams p) {
if (mN.mLargeIcon == null && mN.largeIcon != null) {
mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
}
- boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon;
+ boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient;
if (showLargeIcon) {
contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
- processLargeLegacyIcon(mN.mLargeIcon, contentView);
+ processLargeLegacyIcon(mN.mLargeIcon, contentView, p);
}
return showLargeIcon;
}
@@ -4776,8 +4834,8 @@
* Bind the reply icon.
* @return if the reply icon is visible
*/
- private boolean bindReplyIcon(RemoteViews contentView, boolean hideReplyIcon) {
- boolean actionVisible = !hideReplyIcon;
+ private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) {
+ boolean actionVisible = !p.hideReplyIcon && !p.ambient;
Action action = null;
if (actionVisible) {
action = findReplyAction();
@@ -4787,7 +4845,7 @@
contentView.setViewVisibility(R.id.reply_icon_action, View.VISIBLE);
contentView.setDrawableTint(R.id.reply_icon_action,
false /* targetBackground */,
- getNeutralColor(),
+ getNeutralColor(p),
PorterDuff.Mode.SRC_ATOP);
contentView.setOnClickPendingIntent(R.id.reply_icon_action, action.actionIntent);
contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
@@ -4814,41 +4872,42 @@
return null;
}
- private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
- CharSequence secondaryHeaderText) {
- bindSmallIcon(contentView, ambient);
- bindHeaderAppName(contentView, ambient);
- if (!ambient) {
+ private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
+ bindSmallIcon(contentView, p);
+ bindHeaderAppName(contentView, p);
+ if (!p.ambient) {
// Ambient view does not have these
- bindHeaderText(contentView);
- bindHeaderTextSecondary(contentView, secondaryHeaderText);
- bindHeaderChronometerAndTime(contentView);
- bindProfileBadge(contentView);
+ bindHeaderText(contentView, p);
+ bindHeaderTextSecondary(contentView, p);
+ bindHeaderChronometerAndTime(contentView, p);
+ bindProfileBadge(contentView, p);
+ bindAlertedIcon(contentView, p);
}
- bindActivePermissions(contentView, ambient);
- bindExpandButton(contentView);
+ bindActivePermissions(contentView, p);
+ bindExpandButton(contentView, p);
mN.mUsesStandardHeader = true;
}
- private void bindActivePermissions(RemoteViews contentView, boolean ambient) {
- int color = ambient ? resolveAmbientColor() : getNeutralColor();
+ private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
+ int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p);
contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
}
- private void bindExpandButton(RemoteViews contentView) {
- int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+ private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
+ int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
contentView.setDrawableTint(R.id.expand_button, false, color,
PorterDuff.Mode.SRC_ATOP);
contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
color);
}
- private void bindHeaderChronometerAndTime(RemoteViews contentView) {
+ private void bindHeaderChronometerAndTime(RemoteViews contentView,
+ StandardTemplateParams p) {
if (showsTimeOrChronometer()) {
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
- setTextViewColorSecondary(contentView, R.id.time_divider);
+ setTextViewColorSecondary(contentView, R.id.time_divider, p);
if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
contentView.setLong(R.id.chronometer, "setBase",
@@ -4856,11 +4915,11 @@
contentView.setBoolean(R.id.chronometer, "setStarted", true);
boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
contentView.setChronometerCountDown(R.id.chronometer, countsDown);
- setTextViewColorSecondary(contentView, R.id.chronometer);
+ setTextViewColorSecondary(contentView, R.id.chronometer, p);
} else {
contentView.setViewVisibility(R.id.time, View.VISIBLE);
contentView.setLong(R.id.time, "setTime", mN.when);
- setTextViewColorSecondary(contentView, R.id.time);
+ setTextViewColorSecondary(contentView, R.id.time, p);
}
} else {
// We still want a time to be set but gone, such that we can show and hide it
@@ -4869,36 +4928,36 @@
}
}
- private void bindHeaderText(RemoteViews contentView) {
- CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
- if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
+ private void bindHeaderText(RemoteViews contentView, StandardTemplateParams p) {
+ CharSequence summaryText = p.summaryText;
+ if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
&& mStyle.hasSummaryInHeader()) {
- headerText = mStyle.mSummaryText;
+ summaryText = mStyle.mSummaryText;
}
- if (headerText == null
+ if (summaryText == null
&& mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
&& mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
- headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+ summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
}
- if (headerText != null) {
+ if (summaryText != null) {
// TODO: Remove the span entirely to only have the string with propper formating.
contentView.setTextViewText(R.id.header_text, processTextSpans(
- processLegacyText(headerText)));
- setTextViewColorSecondary(contentView, R.id.header_text);
+ processLegacyText(summaryText)));
+ setTextViewColorSecondary(contentView, R.id.header_text, p);
contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
- setTextViewColorSecondary(contentView, R.id.header_text_divider);
+ setTextViewColorSecondary(contentView, R.id.header_text_divider, p);
}
}
- private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
- if (!TextUtils.isEmpty(secondaryText)) {
+ private void bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p) {
+ if (!TextUtils.isEmpty(p.headerTextSecondary)) {
contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
- processLegacyText(secondaryText)));
- setTextViewColorSecondary(contentView, R.id.header_text_secondary);
+ processLegacyText(p.headerTextSecondary)));
+ setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
- setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
+ setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider, p);
}
}
@@ -4936,23 +4995,27 @@
return String.valueOf(name);
}
- private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
+ private void bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) {
contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
- if (isColorized() && !ambient) {
- setTextViewColorPrimary(contentView, R.id.app_name_text);
+ if (isColorized(p)) {
+ setTextViewColorPrimary(contentView, R.id.app_name_text, p);
} else {
contentView.setTextColor(R.id.app_name_text,
- ambient ? resolveAmbientColor() : getSecondaryTextColor());
+ p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p));
}
}
- private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
+ private boolean isColorized(StandardTemplateParams p) {
+ return p.allowColorization && !p.ambient && mN.isColorized();
+ }
+
+ private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
if (mN.mSmallIcon == null && mN.icon != 0) {
mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
}
contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
- processSmallIconColor(mN.mSmallIcon, contentView, ambient);
+ processSmallIconColor(mN.mSmallIcon, contentView, p);
}
/**
@@ -5027,8 +5090,7 @@
boolean actionHasValidInput = hasValidRemoteInput(action);
validRemoteInput |= actionHasValidInput;
- final RemoteViews button = generateActionButton(action, emphazisedMode,
- p.ambient);
+ final RemoteViews button = generateActionButton(action, emphazisedMode, p);
if (actionHasValidInput && !emphazisedMode) {
// Clear the drawable
button.setInt(R.id.action0, "setBackgroundResource", 0);
@@ -5049,20 +5111,20 @@
View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_1,
processTextSpans(replyText[0]));
- setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
+ setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
big.setViewVisibility(R.id.notification_material_reply_progress,
showSpinner ? View.VISIBLE : View.GONE);
big.setProgressIndeterminateTintList(
R.id.notification_material_reply_progress,
ColorStateList.valueOf(
- isColorized() ? getPrimaryTextColor() : resolveContrastColor()));
+ isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
&& p.maxRemoteInputHistory > 1) {
big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_2,
processTextSpans(replyText[1]));
- setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
+ setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
&& p.maxRemoteInputHistory > 2) {
@@ -5070,7 +5132,7 @@
R.id.notification_material_reply_text_3, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_3,
processTextSpans(replyText[2]));
- setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
+ setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
}
}
}
@@ -5161,18 +5223,23 @@
* @hide
*/
public RemoteViews makeNotificationHeader(boolean ambient) {
- Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
- mN.extras.putBoolean(EXTRA_COLORIZED, false);
+ return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this));
+ }
+
+ /**
+ * Construct a RemoteViews for the final notification header only. This will not be
+ * colorized.
+ *
+ * @param p the template params to inflate this with
+ */
+ private RemoteViews makeNotificationHeader(StandardTemplateParams p) {
+ // Headers on their own are never colorized
+ p.disallowColorization();
RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
- ambient ? R.layout.notification_template_ambient_header
+ p.ambient ? R.layout.notification_template_ambient_header
: R.layout.notification_template_header);
resetNotificationHeader(header);
- bindNotificationHeader(header, ambient, null);
- if (colorized != null) {
- mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
- } else {
- mN.extras.remove(EXTRA_COLORIZED);
- }
+ bindNotificationHeader(header, p);
return header;
}
@@ -5315,24 +5382,15 @@
* @hide
*/
public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
- int color = mN.color;
- mN.color = COLOR_DEFAULT;
- CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
- if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
- CharSequence newSummary = createSummaryText();
- if (!TextUtils.isEmpty(newSummary)) {
- mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
- }
+ StandardTemplateParams p = mParams.reset()
+ .forceDefaultColor()
+ .ambient(false)
+ .fillTextsFrom(this);
+ if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
+ p.summaryText(createSummaryText());
}
-
- RemoteViews header = makeNotificationHeader(false /* ambient */);
+ RemoteViews header = makeNotificationHeader(p);
header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
- if (summary != null) {
- mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
- } else {
- mN.extras.remove(EXTRA_SUB_TEXT);
- }
- mN.color = color;
return header;
}
@@ -5361,7 +5419,7 @@
}
private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
- boolean ambient) {
+ StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
emphazisedMode ? getEmphasizedActionLayoutResource()
@@ -5378,7 +5436,7 @@
// change the background bgColor
CharSequence title = action.title;
ColorStateList[] outResultColor = null;
- int background = resolveBackgroundColor();
+ int background = resolveBackgroundColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
@@ -5386,7 +5444,7 @@
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- setTextViewColorPrimary(button, R.id.action0);
+ setTextViewColorPrimary(button, R.id.action0, p);
int rippleColor;
boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
if (hasColorOverride) {
@@ -5397,11 +5455,12 @@
background, mInNightMode);
button.setTextColor(R.id.action0, textColor);
rippleColor = textColor;
- } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
- rippleColor = resolveContrastColor();
+ } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
+ && mTintActionButtons) {
+ rippleColor = resolveContrastColor(p);
button.setTextColor(R.id.action0, rippleColor);
} else {
- rippleColor = getPrimaryTextColor();
+ rippleColor = getPrimaryTextColor(p);
}
// We only want about 20% alpha for the ripple
rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
@@ -5413,13 +5472,15 @@
} else {
button.setTextViewText(R.id.action0, processTextSpans(
processLegacyText(action.title)));
- if (isColorized() && !ambient) {
- setTextViewColorPrimary(button, R.id.action0);
- } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
+ if (isColorized(p)) {
+ setTextViewColorPrimary(button, R.id.action0, p);
+ } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
button.setTextColor(R.id.action0,
- ambient ? resolveAmbientColor() : resolveContrastColor());
+ p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p));
}
}
+ button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
+ mActions.indexOf(action));
return button;
}
@@ -5525,15 +5586,15 @@
* Apply any necessariy colors to the small icon
*/
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
- boolean ambient) {
+ StandardTemplateParams p) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
int color;
- if (ambient) {
- color = resolveAmbientColor();
- } else if (isColorized()) {
- color = getPrimaryTextColor();
+ if (p.ambient) {
+ color = resolveAmbientColor(p);
+ } else if (isColorized(p)) {
+ color = getPrimaryTextColor(p);
} else {
- color = resolveContrastColor();
+ color = resolveContrastColor(p);
}
if (colorable) {
contentView.setDrawableTint(R.id.icon, false, color,
@@ -5549,11 +5610,12 @@
* if it's grayscale).
*/
// TODO: also check bounds, transparency, that sort of thing.
- private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
+ private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView,
+ StandardTemplateParams p) {
if (largeIcon != null && isLegacy()
&& getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
// resolve color will fall back to the default when legacy
- contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
+ contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p),
PorterDuff.Mode.SRC_ATOP);
}
}
@@ -5564,29 +5626,43 @@
}
}
- int resolveContrastColor() {
- if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
+ int resolveContrastColor(StandardTemplateParams p) {
+ int rawColor = getRawColor(p);
+ if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
return mCachedContrastColor;
}
int color;
int background = mContext.getColor(
com.android.internal.R.color.notification_material_background_color);
- if (mN.color == COLOR_DEFAULT) {
- ensureColors();
+ if (rawColor == COLOR_DEFAULT) {
+ ensureColors(p);
color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
} else {
- color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
+ color = ContrastColorUtil.resolveContrastColor(mContext, rawColor,
background, mInNightMode);
}
if (Color.alpha(color) < 255) {
// alpha doesn't go well for color filters, so let's blend it manually
color = ContrastColorUtil.compositeColors(color, background);
}
- mCachedContrastColorIsFor = mN.color;
+ mCachedContrastColorIsFor = rawColor;
return mCachedContrastColor = color;
}
+ /**
+ * Return the raw color of this Notification, which doesn't necessarily satisfy contrast.
+ *
+ * @see #resolveContrastColor(StandardTemplateParams) for the contrasted color
+ * @param p the template params to inflate this with
+ */
+ private int getRawColor(StandardTemplateParams p) {
+ if (p.forceDefaultColor) {
+ return COLOR_DEFAULT;
+ }
+ return mN.color;
+ }
+
int resolveNeutralColor() {
if (mNeutralColor != COLOR_INVALID) {
return mNeutralColor;
@@ -5602,13 +5678,14 @@
return mNeutralColor;
}
- int resolveAmbientColor() {
- if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
+ int resolveAmbientColor(StandardTemplateParams p) {
+ int rawColor = getRawColor(p);
+ if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) {
return mCachedAmbientColor;
}
- final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, mN.color);
+ final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor);
- mCachedAmbientColorIsFor = mN.color;
+ mCachedAmbientColorIsFor = rawColor;
return mCachedAmbientColor = contrasted;
}
@@ -5657,6 +5734,15 @@
}
/**
+ * Determines whether the platform can generate contextual actions for a notification.
+ * By default this is true.
+ */
+ public Builder setAllowSystemGeneratedContextualActions(boolean allowed) {
+ mN.mAllowSystemGeneratedContextualActions = allowed;
+ return this;
+ }
+
+ /**
* @deprecated Use {@link #build()} instead.
*/
@Deprecated
@@ -5831,9 +5917,9 @@
return R.layout.notification_material_action_tombstone;
}
- private int getBackgroundColor() {
- if (isColorized()) {
- return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
+ private int getBackgroundColor(StandardTemplateParams p) {
+ if (isColorized(p)) {
+ return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
} else {
return COLOR_DEFAULT;
}
@@ -5841,10 +5927,11 @@
/**
* Gets a neutral color that can be used for icons or similar that should not stand out.
+ * @param p the template params to inflate this with
*/
- private int getNeutralColor() {
- if (isColorized()) {
- return getSecondaryTextColor();
+ private int getNeutralColor(StandardTemplateParams p) {
+ if (isColorized(p)) {
+ return getSecondaryTextColor(p);
} else {
return resolveNeutralColor();
}
@@ -5852,9 +5939,10 @@
/**
* Same as getBackgroundColor but also resolved the default color to the background.
+ * @param p the template params to inflate this with
*/
- private int resolveBackgroundColor() {
- int backgroundColor = getBackgroundColor();
+ private int resolveBackgroundColor(StandardTemplateParams p) {
+ int backgroundColor = getBackgroundColor(p);
if (backgroundColor == COLOR_DEFAULT) {
backgroundColor = mContext.getColor(
com.android.internal.R.color.notification_material_background_color);
@@ -5862,10 +5950,6 @@
return backgroundColor;
}
- private boolean isColorized() {
- return mN.isColorized();
- }
-
private boolean shouldTintActionButtons() {
return mTintActionButtons;
}
@@ -5891,7 +5975,7 @@
mBackgroundColor = backgroundColor;
mForegroundColor = foregroundColor;
mTextColorsAreForBackground = COLOR_INVALID;
- ensureColors();
+ ensureColors(mParams.reset().fillTextsFrom(this));
}
/**
@@ -6159,30 +6243,30 @@
}
protected RemoteViews getStandardView(int layoutId) {
- return getStandardView(layoutId, null);
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
+ return getStandardView(layoutId, p, null);
}
+
/**
* Get the standard view for this style.
*
- * @param layoutId The layout id to use
+ * @param layoutId The layout id to use.
+ * @param p the params for this inflation.
* @param result The result where template bind information is saved.
* @return A remoteView for this style.
* @hide
*/
- protected RemoteViews getStandardView(int layoutId, TemplateBindResult result) {
+ protected RemoteViews getStandardView(int layoutId, StandardTemplateParams p,
+ TemplateBindResult result) {
checkBuilder();
- // Nasty.
- CharSequence oldBuilderContentTitle =
- mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
if (mBigContentTitle != null) {
- mBuilder.setContentTitle(mBigContentTitle);
+ p.title = mBigContentTitle;
}
- RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, result);
-
- mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, p,
+ result);
if (mBigContentTitle != null && mBigContentTitle.equals("")) {
contentView.setViewVisibility(R.id.line1, View.GONE);
@@ -6477,12 +6561,13 @@
mBuilder.mN.largeIcon = null;
}
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
- null /* result */);
+ p, null /* result */);
if (mSummaryTextSet) {
contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
mBuilder.processLegacyText(mSummaryText)));
- mBuilder.setTextViewColorSecondary(contentView, R.id.text);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
contentView.setViewVisibility(R.id.text, View.VISIBLE);
}
mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
@@ -6675,24 +6760,24 @@
* @hide
*/
public RemoteViews makeBigContentView() {
-
- // Nasty
- CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
TemplateBindResult result = new TemplateBindResult();
- RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), result);
+ RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), p,
+ result);
contentView.setInt(R.id.big_text, "setImageEndMargin", result.getIconMarginEnd());
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
-
CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
if (TextUtils.isEmpty(bigTextText)) {
// In case the bigtext is null / empty fall back to the normal text to avoid a weird
// experience
- bigTextText = mBuilder.processLegacyText(text);
+ bigTextText = mBuilder.processLegacyText(
+ mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT));
}
- applyBigTextContentView(mBuilder, contentView, bigTextText);
+ contentView.setTextViewText(R.id.big_text, mBuilder.processTextSpans(bigTextText));
+ 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());
return contentView;
}
@@ -6710,14 +6795,6 @@
return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText()));
}
- static void applyBigTextContentView(Builder builder,
- RemoteViews contentView, CharSequence bigTextText) {
- contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
- builder.setTextViewColorSecondary(contentView, R.id.big_text);
- contentView.setViewVisibility(R.id.big_text,
- TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
- contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
- }
}
/**
@@ -7201,24 +7278,26 @@
isOneToOne = !isGroupConversation();
}
TemplateBindResult bindResult = new TemplateBindResult();
+ StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).title(
+ conversationTitle).text(null)
+ .hideLargeIcon(hideRightIcons || isOneToOne)
+ .hideReplyIcon(hideRightIcons)
+ .headerTextSecondary(conversationTitle);
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getMessagingLayoutResource(),
- mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
- .hideLargeIcon(hideRightIcons || isOneToOne)
- .hideReplyIcon(hideRightIcons)
- .headerTextSecondary(conversationTitle),
+ p,
bindResult);
addExtras(mBuilder.mN.extras);
// also update the end margin if there is an image
contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
bindResult.getIconMarginEnd());
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
- mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
- : mBuilder.resolveContrastColor());
+ mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+ : mBuilder.resolveContrastColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
- mBuilder.getPrimaryTextColor());
+ mBuilder.getPrimaryTextColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
- mBuilder.getSecondaryTextColor());
+ mBuilder.getSecondaryTextColor(p));
contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
displayImagesAtEnd);
contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
@@ -7682,15 +7761,9 @@
* @hide
*/
public RemoteViews makeBigContentView() {
- // Remove the content text so it disappears unless you have a summary
- // Nasty
- CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
TemplateBindResult result = new TemplateBindResult();
- RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), result);
-
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
+ RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result);
int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
@@ -7737,7 +7810,7 @@
contentView.setViewVisibility(rowIds[i], View.VISIBLE);
contentView.setTextViewText(rowIds[i],
mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
- mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
+ mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
handleInboxImageMargin(contentView, rowIds[i], first,
result.getIconMarginEnd());
@@ -7974,7 +8047,7 @@
}
private void bindMediaActionButton(RemoteViews container, @IdRes int buttonId,
- Action action, int color) {
+ Action action, StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
container.setViewVisibility(buttonId, View.VISIBLE);
container.setImageViewIcon(buttonId, action.getIcon());
@@ -7985,8 +8058,8 @@
Configuration currentConfig = resources.getConfiguration();
boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
- int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
- ? color
+ int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized(p)
+ ? getActionColor(p)
: ContrastColorUtil.resolveColor(mBuilder.mContext,
Notification.COLOR_DEFAULT, inNightMode);
@@ -8008,8 +8081,10 @@
}
private RemoteViews makeMediaContentView() {
+ StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+ mBuilder);
RemoteViews view = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_media, false, /* hasProgress */
+ R.layout.notification_template_material_media, p,
null /* result */);
final int numActions = mBuilder.mActions.size();
@@ -8024,7 +8099,7 @@
for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
if (i < numActionsToShow) {
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
- bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, getActionColor());
+ bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p);
} else {
view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
@@ -8039,9 +8114,9 @@
return view;
}
- private int getActionColor() {
- return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
- : mBuilder.resolveContrastColor();
+ private int getActionColor(StandardTemplateParams p) {
+ return mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+ : mBuilder.resolveContrastColor(p);
}
private RemoteViews makeMediaBigContentView() {
@@ -8053,13 +8128,14 @@
if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
return null;
}
+ StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+ mBuilder);
RemoteViews big = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_big_media, false, null /* result */);
+ R.layout.notification_template_material_big_media, p , null /* result */);
for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
if (i < actionCount) {
- bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i),
- getActionColor());
+ bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
} else {
big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
@@ -8187,7 +8263,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();
@@ -8315,10 +8391,10 @@
// Need to clone customContent before adding, because otherwise it can no longer be
// parceled independently of remoteViews.
customContent = customContent.clone();
- customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
+ customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
remoteViews.removeAllViews(id);
remoteViews.addView(id, customContent);
- remoteViews.setReapplyDisallowed();
+ remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
}
return remoteViews;
}
@@ -9858,17 +9934,23 @@
CharSequence title;
CharSequence text;
CharSequence headerTextSecondary;
+ CharSequence summaryText;
int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
boolean hideLargeIcon;
boolean hideReplyIcon;
+ boolean allowColorization = true;
+ boolean forceDefaultColor = false;
final StandardTemplateParams reset() {
hasProgress = true;
ambient = false;
title = null;
text = null;
+ summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
+ allowColorization = true;
+ forceDefaultColor = false;
return this;
}
@@ -9887,6 +9969,11 @@
return this;
}
+ final StandardTemplateParams summaryText(CharSequence text) {
+ this.summaryText = text;
+ return this;
+ }
+
final StandardTemplateParams headerTextSecondary(CharSequence text) {
this.headerTextSecondary = text;
return this;
@@ -9902,6 +9989,16 @@
return this;
}
+ final StandardTemplateParams disallowColorization() {
+ this.allowColorization = false;
+ return this;
+ }
+
+ final StandardTemplateParams forceDefaultColor() {
+ this.forceDefaultColor = true;
+ return this;
+ }
+
final StandardTemplateParams ambient(boolean ambient) {
Preconditions.checkState(title == null && text == null, "must set ambient before text");
this.ambient = ambient;
@@ -9918,6 +10015,7 @@
text = extras.getCharSequence(EXTRA_TEXT);
}
this.text = b.processLegacyText(text, ambient);
+ this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
return this;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 89ec19b..25fa897 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1424,7 +1424,62 @@
return other.priorityCategories == priorityCategories
&& other.priorityCallSenders == priorityCallSenders
&& other.priorityMessageSenders == priorityMessageSenders
- && other.suppressedVisualEffects == suppressedVisualEffects;
+ && suppressedVisualEffectsEqual(suppressedVisualEffects,
+ other.suppressedVisualEffects);
+ }
+
+
+ private boolean suppressedVisualEffectsEqual(int suppressedEffects,
+ int otherSuppressedVisualEffects) {
+ if (suppressedEffects == otherSuppressedVisualEffects) {
+ return true;
+ }
+
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) {
+ suppressedEffects |= SUPPRESSED_EFFECT_PEEK;
+ }
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) {
+ suppressedEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ suppressedEffects |= SUPPRESSED_EFFECT_LIGHTS;
+ suppressedEffects |= SUPPRESSED_EFFECT_AMBIENT;
+ }
+
+ if ((otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) {
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
+ }
+ if ((otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) {
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
+ }
+
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON)
+ != (otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON)) {
+ int currSuppressedEffects = (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0
+ ? otherSuppressedVisualEffects : suppressedEffects;
+ if ((currSuppressedEffects & SUPPRESSED_EFFECT_PEEK) == 0) {
+ return false;
+ }
+ }
+
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF)
+ != (otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF)) {
+ int currSuppressedEffects = (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0
+ ? otherSuppressedVisualEffects : suppressedEffects;
+ if ((currSuppressedEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0
+ || (currSuppressedEffects & SUPPRESSED_EFFECT_LIGHTS) == 0
+ || (currSuppressedEffects & SUPPRESSED_EFFECT_AMBIENT) == 0) {
+ return false;
+ }
+ }
+
+ int thisWithoutOldEffects = suppressedEffects
+ & ~SUPPRESSED_EFFECT_SCREEN_ON
+ & ~SUPPRESSED_EFFECT_SCREEN_OFF;
+ int otherWithoutOldEffects = otherSuppressedVisualEffects
+ & ~SUPPRESSED_EFFECT_SCREEN_ON
+ & ~SUPPRESSED_EFFECT_SCREEN_OFF;
+ return thisWithoutOldEffects == otherWithoutOldEffects;
}
@Override
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index a2dae3b..0abc998 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -127,8 +127,8 @@
if (obj instanceof Person) {
final Person other = (Person) obj;
return Objects.equals(mName, other.mName)
- && mIcon == null ? other.mIcon == null :
- (other.mIcon != null && mIcon.sameAs(other.mIcon))
+ && (mIcon == null ? other.mIcon == null :
+ (other.mIcon != null && mIcon.sameAs(other.mIcon)))
&& Objects.equals(mUri, other.mUri)
&& Objects.equals(mKey, other.mKey)
&& mIsBot == other.mIsBot
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index 9df4fff..95d5d19 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -32,13 +32,11 @@
public final long rssInBytes;
public final long cacheInBytes;
public final long swapInBytes;
- // TODO(rslawik): Delete this field once ProcessMemoryHighWaterMark is ready.
- public final long rssHighWatermarkInBytes;
public final long startTimeNanos;
public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
long pgmajfault, long rssInBytes, long cacheInBytes,
- long swapInBytes, long rssHighWatermarkInBytes, long startTimeNanos) {
+ long swapInBytes, long startTimeNanos) {
this.uid = uid;
this.processName = processName;
this.oomScore = oomScore;
@@ -47,7 +45,6 @@
this.rssInBytes = rssInBytes;
this.cacheInBytes = cacheInBytes;
this.swapInBytes = swapInBytes;
- this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
this.startTimeNanos = startTimeNanos;
}
@@ -60,7 +57,6 @@
rssInBytes = in.readLong();
cacheInBytes = in.readLong();
swapInBytes = in.readLong();
- rssHighWatermarkInBytes = in.readLong();
startTimeNanos = in.readLong();
}
@@ -91,7 +87,6 @@
parcel.writeLong(rssInBytes);
parcel.writeLong(cacheInBytes);
parcel.writeLong(swapInBytes);
- parcel.writeLong(rssHighWatermarkInBytes);
parcel.writeLong(startTimeNanos);
}
}
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 0123551..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.IIntelligenceManager;
-import android.view.intelligence.IntelligenceManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -199,6 +200,7 @@
private SystemServiceRegistry() { }
static {
+ //CHECKSTYLE:OFF IndentationCheck
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
@@ -385,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
@@ -1050,15 +1060,20 @@
return new AutofillManager(ctx.getOuterContext(), service);
}});
- registerService(Context.INTELLIGENCE_MANAGER_SERVICE, IntelligenceManager.class,
- new CachedServiceFetcher<IntelligenceManager>() {
+ registerService(Context.CONTENT_CAPTURE_MANAGER_SERVICE, ContentCaptureManager.class,
+ new CachedServiceFetcher<ContentCaptureManager>() {
@Override
- public IntelligenceManager createService(ContextImpl ctx)
+ public ContentCaptureManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
// Get the services without throwing as this is an optional feature
- IBinder b = ServiceManager.getService(Context.INTELLIGENCE_MANAGER_SERVICE);
- IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
- return new IntelligenceManager(ctx.getOuterContext(), service);
+ Context outerContext = ctx.getOuterContext();
+ if (outerContext.isContentCaptureSupported()) {
+ IBinder b = ServiceManager
+ .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
+ return new ContentCaptureManager(outerContext, service);
+ }
+ return null;
}});
registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
@@ -1138,6 +1153,7 @@
throws ServiceNotFoundException {
return new RoleManager(ctx.getOuterContext());
}});
+ //CHECKSTYLE:ON IndentationCheck
}
/**
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index e33d1fe..f0f7d89 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.SystemApi;
import android.app.slice.Slice;
import android.content.ComponentName;
import android.content.Context;
@@ -36,6 +37,7 @@
import android.util.AttributeSet;
import android.util.Printer;
import android.util.Xml;
+import android.view.SurfaceHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -79,6 +81,7 @@
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
final String mSettingsSliceUri;
+ final boolean mSupportMultipleDisplays;
/**
* Constructor.
@@ -143,6 +146,9 @@
false);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
+ mSupportMultipleDisplays = sa.getBoolean(
+ com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
+ false);
sa.recycle();
} catch (NameNotFoundException e) {
@@ -163,6 +169,7 @@
mShowMetadataInPreview = source.readInt() != 0;
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
+ mSupportMultipleDisplays = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -324,7 +331,9 @@
* @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
* @see WallpaperService.Engine#isInAmbientMode()
* @return {@code true} if wallpaper can draw when in ambient mode.
+ * @hide
*/
+ @SystemApi
public boolean supportsAmbientMode() {
return mSupportsAmbientMode;
}
@@ -358,6 +367,19 @@
return Uri.parse(mSettingsSliceUri);
}
+ /**
+ * Returns whether this wallpaper service can support multiple engines to render on each surface
+ * independently. An example use case is a multi-display set-up where the wallpaper service can
+ * render surfaces to each of the connected displays.
+ *
+ * @see WallpaperService#onCreateEngine()
+ * @see WallpaperService.Engine#onCreate(SurfaceHolder)
+ * @return {@code true} if multiple engines can render independently on each surface.
+ */
+ public boolean supportsMultipleDisplays() {
+ return mSupportMultipleDisplays;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Service:");
mService.dump(pw, prefix + " ");
@@ -387,6 +409,7 @@
dest.writeInt(mShowMetadataInPreview ? 1 : 0);
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
+ dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index bebe79e..27ae0b0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1485,7 +1485,7 @@
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.getWidthHint();
+ return sGlobals.mService.getWidthHint(mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1511,7 +1511,7 @@
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.getHeightHint();
+ return sGlobals.mService.getHeightHint(mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1572,7 +1572,7 @@
throw new RuntimeException(new DeadSystemException());
} else {
sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getDisplayId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1597,7 +1597,8 @@
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName());
+ sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(),
+ mContext.getDisplayId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1897,23 +1898,33 @@
* @hide
*/
public static ComponentName getDefaultWallpaperComponent(Context context) {
+ ComponentName cn = null;
+
String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT);
if (!TextUtils.isEmpty(flat)) {
- final ComponentName cn = ComponentName.unflattenFromString(flat);
- if (cn != null) {
- return cn;
+ cn = ComponentName.unflattenFromString(flat);
+ }
+
+ if (cn == null) {
+ flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
+ if (!TextUtils.isEmpty(flat)) {
+ cn = ComponentName.unflattenFromString(flat);
}
}
- flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
- if (!TextUtils.isEmpty(flat)) {
- final ComponentName cn = ComponentName.unflattenFromString(flat);
- if (cn != null) {
- return cn;
+ // Check if the package exists
+ if (cn != null) {
+ try {
+ final PackageManager packageManager = context.getPackageManager();
+ packageManager.getPackageInfo(cn.getPackageName(),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ } catch (PackageManager.NameNotFoundException e) {
+ cn = null;
}
}
- return null;
+ return cn;
}
/**
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 78b7d48..257122d 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);
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
new file mode 100644
index 0000000..f39a5f4
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.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)
+ * .setAdminPackageName(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 setAdminPackageName(String packageName) {
+ mAdminPackageName = packageName;
+ 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 0f26b1c..e826250 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,6 +53,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -87,6 +88,7 @@
import com.android.org.conscrypt.TrustedCertificateStore;
import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1988,6 +1990,48 @@
public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
/**
+ * Callback used in {@link #installSystemUpdate} to indicate that there was an error while
+ * trying to install an update.
+ */
+ public abstract static class InstallUpdateCallback {
+ /** Represents an unknown error while trying to install an update. */
+ public static final int UPDATE_ERROR_UNKNOWN = 1;
+
+ /** Represents the update file being intended for different OS version. */
+ public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2;
+
+ /**
+ * Represents the update file being wrong, i.e. payloads are mismatched, wrong compressions
+ * method.
+ */
+ public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
+
+ /** Represents that the file could not be found. */
+ public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4;
+
+ /** Represents the battery being too low to apply an update. */
+ public static final int UPDATE_ERROR_BATTERY_LOW = 5;
+
+ /** Method invoked when there was an error while installing an update. */
+ public void onInstallUpdateError(
+ @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "UPDATE_ERROR_" }, value = {
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+ InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InstallUpdateCallbackErrorConstants {}
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -6854,7 +6898,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface CreateAndManageUserFlags {}
-
/**
* Called by a device owner to create a user with the specified name and a given component of
* the calling package as profile owner. The UserHandle returned by this method should not be
@@ -9885,6 +9928,62 @@
}
/**
+ * Called by device owner to install a system update from the given file. The device will be
+ * rebooted in order to finish installing the update. Note that if the device is rebooted, this
+ * doesn't necessarily mean that the update has been applied successfully. The caller should
+ * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
+ * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot,
+ * the caller will be notified by {@link InstallUpdateCallback}. If device does not have
+ * sufficient battery level, the installation will fail with error {@link
+ * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
+ *
+ * @param admin The {@link DeviceAdminReceiver} that this request is associated with.
+ * @param updateFilePath An Uri of the file that contains the update. The file should be
+ * readable by the calling app.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback A callback object that will inform the caller when installing an update
+ * fails.
+ */
+ public void installSystemUpdate(
+ @NonNull ComponentName admin, @NonNull Uri updateFilePath,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull InstallUpdateCallback callback) {
+ throwIfParentInstance("installUpdate");
+ if (mService == null) {
+ return;
+ }
+ try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver()
+ .openFileDescriptor(updateFilePath, "r")) {
+ mService.installUpdateFromFile(
+ admin, fileDescriptor, new StartInstallingUpdateCallback.Stub() {
+ @Override
+ public void onStartInstallingUpdateError(
+ int errorCode, String errorMessage) {
+ executeCallback(errorCode, errorMessage, executor, callback);
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, e);
+ executeCallback(
+ InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e),
+ executor, callback);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ executeCallback(
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
+ executor, callback);
+ }
+ }
+
+ private void executeCallback(int errorCode, String errorMessage,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull InstallUpdateCallback callback) {
+ executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage));
+ }
+
+ /**
* Returns the system-wide Private DNS mode.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with.
@@ -9951,4 +10050,140 @@
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Whitelists a package that is allowed to access cross profile calendar APIs.
+ *
+ * <p>Called by a profile owner of a managed profile.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName name of the package to be whitelisted.
+ * @throws SecurityException if {@code admin} is not a profile owner.
+ *
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ */
+ public void addCrossProfileCalendarPackage(@NonNull ComponentName admin,
+ @NonNull String packageName) {
+ throwIfParentInstance("addCrossProfileCalendarPackage");
+ if (mService != null) {
+ try {
+ mService.addCrossProfileCalendarPackage(admin, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Removes a package that was allowed to access cross profile calendar APIs
+ * from the whitelist.
+ *
+ * <p>Called by a profile owner of a managed profile.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName name of the package to be removed from the whitelist.
+ * @return {@code true} if the package is successfully removed from the whitelist,
+ * {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a profile owner.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ */
+ public boolean removeCrossProfileCalendarPackage(@NonNull ComponentName admin,
+ @NonNull String packageName) {
+ throwIfParentInstance("removeCrossProfileCalendarPackage");
+ if (mService != null) {
+ try {
+ return mService.removeCrossProfileCalendarPackage(admin, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+ *
+ * <p>Called by a profile owner of a managed profile.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @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.
+ * @throws SecurityException if {@code admin} is not a profile owner.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ */
+ public @NonNull Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) {
+ throwIfParentInstance("getCrossProfileCalendarPackages");
+ if (mService != null) {
+ try {
+ return new ArraySet<>(mService.getCrossProfileCalendarPackages(admin));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns if a package is 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.
+ *
+ * @param packageName the name of the package
+ * @return {@code true} if the package is whitelisted to access cross profile calendar APIs.
+ * {@code false} otherwise.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ * @hide
+ */
+ public @NonNull boolean isPackageAllowedToAccessCalendar(@NonNull String packageName) {
+ throwIfParentInstance("isPackageAllowedToAccessCalendar");
+ if (mService != null) {
+ try {
+ return mService.isPackageAllowedToAccessCalendarForUser(packageName,
+ 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();
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 918c127..fcf74ee 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -20,6 +20,7 @@
import android.app.admin.NetworkEvent;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
+import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.PasswordMetrics;
@@ -419,4 +420,12 @@
String getGlobalPrivateDnsHost(in ComponentName admin);
void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
+
+ void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener);
+
+ void addCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+ boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+ List<String> getCrossProfileCalendarPackages(in ComponentName admin);
+ boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
+ List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
}
diff --git a/core/java/android/app/admin/StartInstallingUpdateCallback.aidl b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
new file mode 100644
index 0000000..df04707
--- /dev/null
+++ b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app.admin;
+
+/**
+* Callback used between {@link DevicePolicyManager} and {@link DevicePolicyManagerService} to
+* indicate that starting installing an update is finished.
+* {@hide}
+*/
+oneway interface StartInstallingUpdateCallback {
+ void onStartInstallingUpdateError(int errorCode, String errorMessage);
+}
\ No newline at end of file
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
index 1c9a43a..673d85f 100644
--- a/core/java/android/app/backup/OWNERS
+++ b/core/java/android/app/backup/OWNERS
@@ -1,7 +1,6 @@
-artikz@google.com
+anniemeng@google.com
brufino@google.com
bryanmawhinney@google.com
ctate@google.com
jorlow@google.com
-mkarpinski@google.com
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5514851..3f34803 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -193,10 +193,6 @@
/** @hide */
public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
/** @hide */
- public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E;
- /** @hide */
- public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F;
- /** @hide */
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
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/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 237082e..2f0b44f 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.EventLog;
/**
@@ -30,6 +31,8 @@
*/
public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
+ private static final int MAX_DESCRIPTOR_SIZE = 2048;
+
private final String mName;
private final String mDescription;
private final String mProvider;
@@ -55,6 +58,12 @@
mDescription = description;
mProvider = provider;
mSubclass = subclass;
+
+ if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) {
+ EventLog.writeEvent(0x534e4554, "119819889", -1, "");
+ throw new IllegalArgumentException("descriptors must be not null and shorter than "
+ + MAX_DESCRIPTOR_SIZE);
+ }
mDescriptors = descriptors.clone();
}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 11f8ab7..e3672a7 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -52,8 +52,7 @@
@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
public final class BluetoothManager {
private static final String TAG = "BluetoothManager";
- private static final boolean DBG = true;
- private static final boolean VDBG = true;
+ private static final boolean DBG = false;
private final BluetoothAdapter mAdapter;
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 7988008..2174255 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -116,6 +116,9 @@
*/
@Nullable
public byte[] getManufacturerSpecificData(int manufacturerId) {
+ if (mManufacturerSpecificData == null) {
+ return null;
+ }
return mManufacturerSpecificData.get(manufacturerId);
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 145c9273..0f72fd3 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -586,7 +586,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;
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 949cdd6..7d5202d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,7 +42,6 @@
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -98,8 +97,7 @@
*
* @hide
*/
- public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
- .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+ public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage();
/**
* Special filesystem path prefix which indicates that a path should be
@@ -3244,18 +3242,8 @@
Objects.requireNonNull(uri);
Objects.requireNonNull(size);
- // Older apps might be relying on mutable results, so only consider
- // giving them hardware bitmaps once they target Q or higher. If modern
- // apps need mutable thumbnails, they can always roll their own logic.
- final int allocator;
- if (getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- allocator = ImageDecoder.ALLOCATOR_DEFAULT;
- } else {
- allocator = ImageDecoder.ALLOCATOR_SOFTWARE;
- }
-
try (ContentProviderClient client = acquireContentProviderClient(uri)) {
- return loadThumbnail(client, uri, size, signal, allocator);
+ return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cec8ef5..d7d3cb5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -741,16 +741,22 @@
/** Return the name of this application's package. */
public abstract String getPackageName();
- /** @hide Return the name of the base context this context is derived from. */
+ /**
+ * @hide Return the name of the base context this context is derived from.
+ * This is the same as {@link #getOpPackageName()} except in
+ * cases where system components are loaded into other app processes, in which
+ * case {@link #getOpPackageName()} will be the name of the primary package in
+ * that process (so that app ops uid verification will work with the name).
+ */
@UnsupportedAppUsage
public abstract String getBasePackageName();
- /** @hide Return the package name that should be used for app ops calls from
- * this context. This is the same as {@link #getBasePackageName()} except in
- * cases where system components are loaded into other app processes, in which
- * case this will be the name of the primary package in that process (so that app
- * ops uid verification will work with the name). */
- @TestApi
+ /**
+ * Return the package name that should be used for {@link android.app.AppOpsManager} calls from
+ * this context, so that app ops manager's uid verification will work with the name.
+ * <p>
+ * This is not generally intended for third party application developers.
+ */
public abstract String getOpPackageName();
/** Return the full application info for this context's package. */
@@ -2997,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
@@ -3120,6 +3131,7 @@
//@hide: HDMI_CONTROL_SERVICE,
INPUT_SERVICE,
DISPLAY_SERVICE,
+ //@hide COLOR_DISPLAY_SERVICE,
USER_SERVICE,
RESTRICTIONS_SERVICE,
APP_OPS_SERVICE,
@@ -3927,12 +3939,12 @@
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
- * Official published name of the intelligence service.
+ * Official published name of the content capture service.
*
* @hide
* @see #getSystemService(String)
*/
- public static final String INTELLIGENCE_MANAGER_SERVICE = "intelligence";
+ public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
/**
* Use with {@link #getSystemService(String)} to access the
@@ -4101,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)
@@ -5217,6 +5239,25 @@
}
/**
+ * Checks whether this context supports content capture.
+ *
+ * @hide
+ */
+ // NOTE: for now we just need to check if it's supported so we can optimize calls that can be
+ // skipped when it isn't. Eventually, we might need a full
+ // ContentCaptureManager.ContentCaptureClient interface (as it's done with AutofillClient).
+ //
+ public boolean isContentCaptureSupported() {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public void setContentCaptureSupported(@SuppressWarnings("unused") boolean supported) {
+ }
+
+ /**
* Throws an exception if the Context is using system resources,
* which are non-runtime-overlay-themable and may show inconsistent UI.
* @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 2db44b4..26ed3b7 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1049,4 +1049,20 @@
mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
}
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean isContentCaptureSupported() {
+ return mBase.isContentCaptureSupported();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setContentCaptureSupported(boolean supported) {
+ mBase.setContentCaptureSupported(supported);
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 10c6bc6..b6f9d15 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1757,30 +1757,6 @@
"android.intent.action.MANAGE_APP_PERMISSIONS";
/**
- * Activity action: Launch UI to manage a specific permissions of an app.
- * <p>
- * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
- * will be managed by the launched UI.
- * </p>
- * <p>
- * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
- * that should be managed by the launched UI.
- * </p>
- * <p>
- * Output: Nothing.
- * </p>
- *
- * @see #EXTRA_PACKAGE_NAME
- * @see #EXTRA_PERMISSION_NAME
- *
- * @hide
- */
- @SystemApi
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_MANAGE_APP_PERMISSION =
- "android.intent.action.MANAGE_APP_PERMISSION";
-
- /**
* Activity action: Launch UI to manage permissions.
* <p>
* Input: Nothing.
@@ -1940,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
@@ -3586,6 +3565,27 @@
public static final String
ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
+
+ /**
+ * Activity Action: Perform text translation.
+ * <p>
+ * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
+
+ /**
+ * Activity Action: Define the meaning of the selected word(s).
+ * <p>
+ * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
/**
* Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
* exisiting sensor being disconnected.
@@ -4092,6 +4092,18 @@
*/
public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE";
+ /**
+ * Broadcast Action: Indicates that a new device customization has been
+ * downloaded and applied (packages installed, runtime resource overlays
+ * enabled, xml files copied, ...), and that it is time for components that
+ * need to for example clear their caches to do so now.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_DEVICE_CUSTOMIZATION_READY =
+ "android.intent.action.DEVICE_CUSTOMIZATION_READY";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -5230,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>
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4e110da..07d6e47 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,7 +32,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.text.TextUtils;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -464,16 +463,6 @@
public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
/**
- * Value for {@link #privateFlags}: Set to true if the application has been
- * installed using the forward lock option.
- *
- * NOTE: DO NOT CHANGE THIS VALUE! It is saved in packages.xml.
- *
- * {@hide}
- */
- public static final int PRIVATE_FLAG_FORWARD_LOCK = 1<<2;
-
- /**
* Value for {@link #privateFlags}: set to {@code true} if the application
* is permitted to hold privileged permissions.
*
@@ -651,7 +640,6 @@
PRIVATE_FLAG_CANT_SAVE_STATE,
PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
PRIVATE_FLAG_DIRECT_BOOT_AWARE,
- PRIVATE_FLAG_FORWARD_LOCK,
PRIVATE_FLAG_HAS_DOMAIN_URLS,
PRIVATE_FLAG_HIDDEN,
PRIVATE_FLAG_INSTANT,
@@ -1315,7 +1303,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);
@@ -1782,7 +1770,7 @@
* is on the package whitelist.
*
* @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT}
- * if nothing configured.
+ * if nothing configured.
* @hide
*/
public void maybeUpdateHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
@@ -1843,17 +1831,6 @@
return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- /** @hide */
- public boolean isExternalAsec() {
- return TextUtils.isEmpty(volumeUuid) && isExternal();
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public boolean isForwardLocked() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
-
/**
* True if the application is installed as an instant app.
* @hide
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d0eff2e..dbea821 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -676,4 +676,6 @@
String getSystemTextClassifierPackageName();
boolean isPackageStateProtected(String packageName, int userId);
+
+ void sendDeviceCustomizationReadyBroadcast();
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ecdd810..099d15a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -22,6 +22,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Overall information about the contents of a package. This corresponds
* to all of the information collected from AndroidManifest.xml.
@@ -204,7 +207,10 @@
* {@link PackageManager#GET_PERMISSIONS} was set. This list includes
* all permissions requested, even those that were not granted or known
* by the system at install time.
+ *
+ * @deprecated Use {@link #usesPermissions}
*/
+ @Deprecated
public String[] requestedPermissions;
/**
@@ -214,10 +220,23 @@
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
* the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+ *
+ * @deprecated Use {@link #usesPermissions}
*/
+ @Deprecated
public int[] requestedPermissionsFlags;
/**
+ * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
+ * <uses-permission>} tags included under <manifest>,
+ * or null if there were none. This is only filled in if the flag
+ * {@link PackageManager#GET_PERMISSIONS} was set. This list includes
+ * all permissions requested, even those that were not granted or known
+ * by the system at install time.
+ */
+ public UsesPermissionInfo[] usesPermissions;
+
+ /**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
@@ -456,6 +475,7 @@
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
dest.writeIntArray(requestedPermissionsFlags);
+ dest.writeTypedArray(usesPermissions, parcelableFlags);
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -520,6 +540,7 @@
permissions = source.createTypedArray(PermissionInfo.CREATOR);
requestedPermissions = source.createStringArray();
requestedPermissionsFlags = source.createIntArray();
+ usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR);
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f90199..07672d9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1374,12 +1374,6 @@
}
/** {@hide} */
- public void setInstallFlagsInternal() {
- installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
- }
-
- /** {@hide} */
@SystemApi
public void setAllowDowngrade(boolean allowDowngrade) {
if (allowDowngrade) {
@@ -1390,12 +1384,6 @@
}
/** {@hide} */
- public void setInstallFlagsExternal() {
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- installFlags &= ~PackageManager.INSTALL_INTERNAL;
- }
-
- /** {@hide} */
public void setInstallFlagsForcePermissionPrompt() {
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
}
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 361beba..b7df2bf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -700,10 +700,8 @@
/** @hide */
@IntDef(flag = true, prefix = { "INSTALL_" }, value = {
- INSTALL_FORWARD_LOCK,
INSTALL_REPLACE_EXISTING,
INSTALL_ALLOW_TEST,
- INSTALL_EXTERNAL,
INSTALL_INTERNAL,
INSTALL_FROM_ADB,
INSTALL_ALL_USERS,
@@ -721,17 +719,6 @@
public @interface InstallFlags {}
/**
- * Flag parameter for {@link #installPackage} to indicate that this package
- * should be installed as forward locked, i.e. only the app itself should
- * have access to its code and non-resource assets.
- *
- * @deprecated new installs into ASEC containers are no longer supported.
- * @hide
- */
- @Deprecated
- public static final int INSTALL_FORWARD_LOCK = 0x00000001;
-
- /**
* Flag parameter for {@link #installPackage} to indicate that you want to
* replace an already installed package, if one exists.
*
@@ -750,17 +737,6 @@
/**
* Flag parameter for {@link #installPackage} to indicate that this package
- * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
- *
- * @deprecated new installs into ASEC containers are no longer supported;
- * use adoptable storage instead.
- * @hide
- */
- @Deprecated
- public static final int INSTALL_EXTERNAL = 0x00000008;
-
- /**
- * Flag parameter for {@link #installPackage} to indicate that this package
* must be installed to internal storage.
*
* @hide
@@ -1521,14 +1497,6 @@
/**
* Error code that is passed to the {@link IPackageMoveObserver} if the
- * specified package cannot be moved since its forward locked.
- *
- * @hide
- */
- public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
-
- /**
- * Error code that is passed to the {@link IPackageMoveObserver} if the
* specified package cannot be moved to the specified location.
*
* @hide
@@ -5404,6 +5372,10 @@
public abstract void removePackageFromPreferred(String packageName);
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Retrieve the list of all currently configured preferred packages. The
* first package on the list is the most preferred, the last is the least
* preferred.
@@ -5412,6 +5384,7 @@
* @return A List of PackageInfo objects, one for each preferred
* application, in order of preference.
*/
+ @Deprecated
public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
/**
@@ -5438,11 +5411,16 @@
ComponentName[] set, ComponentName activity);
/**
+ * @deprecated This is a protected API that should not have been available
+ * to third party applications. It is the platform's responsibility for
+ * assigning preferred activities and this cannot be directly modified.
+ *
* Same as {@link #addPreferredActivity(IntentFilter, int,
ComponentName[], ComponentName)}, but with a specific userId to apply the preference
to.
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public void addPreferredActivityAsUser(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, @UserIdInt int userId) {
@@ -5476,6 +5454,10 @@
ComponentName[] set, ComponentName activity);
/**
+ * @deprecated This is a protected API that should not have been available
+ * to third party applications. It is the platform's responsibility for
+ * assigning preferred activities and this cannot be directly modified.
+ *
* Replaces an existing preferred activity mapping to the system, and if that were not present
* adds a new preferred activity. This will be used to automatically select the given activity
* component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5491,6 +5473,7 @@
*
* @hide
*/
+ @Deprecated
@SystemApi
public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
@NonNull List<ComponentName> set, @NonNull ComponentName activity) {
@@ -5508,6 +5491,10 @@
}
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Remove all preferred activity mappings, previously added with
* {@link #addPreferredActivity}, from the
* system whose activities are implemented in the given package name.
@@ -5516,9 +5503,14 @@
* @param packageName The name of the package whose preferred activity
* mappings are to be removed.
*/
+ @Deprecated
public abstract void clearPackagePreferredActivities(String packageName);
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Retrieve all preferred activities, previously added with
* {@link #addPreferredActivity}, that are
* currently registered with the system.
@@ -5535,6 +5527,7 @@
* (the number of distinct IntentFilter records, not the number of unique
* activity components) that were found.
*/
+ @Deprecated
public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
@NonNull List<ComponentName> outActivities, String packageName);
@@ -6441,4 +6434,18 @@
"isPackageStateProtected not implemented in subclass");
}
+ /**
+ * Notify to the rest of the system that a new device configuration has
+ * been prepared and that it is time to refresh caches.
+ *
+ * @see android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY
+ *
+ * @hide
+ */
+ @SystemApi
+ public void sendDeviceCustomizationReadyBroadcast() {
+ throw new UnsupportedOperationException(
+ "sendDeviceCustomizationReadyBroadcast not implemented in subclass");
+ }
+
}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 6f49cc4..b49c447 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.util.function.TriFunction;
@@ -37,6 +38,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
/**
* Package manager local system service interface.
@@ -735,4 +737,22 @@
/** Returns {@code true} if the given user requires extra badging for icons. */
public abstract boolean userNeedsBadging(int userId);
+
+ /**
+ * Perform the given action for each package.
+ * Note that packages lock will be held while performin the actions.
+ *
+ * @param actionLocked action to be performed
+ */
+ public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
+
+ /** Returns the list of enabled components */
+ public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
+
+ /** Returns the list of disabled components */
+ public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
+
+ /** Returns whether the given package is enabled for the given user */
+ public abstract @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2fcf1dd..ac18dca 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -785,18 +785,23 @@
pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
}
}
- N = p.requestedPermissions.size();
+ N = p.usesPermissionInfos.size();
if (N > 0) {
pi.requestedPermissions = new String[N];
pi.requestedPermissionsFlags = new int[N];
+ pi.usesPermissions = new UsesPermissionInfo[N];
for (int i=0; i<N; i++) {
- final String perm = p.requestedPermissions.get(i);
+ UsesPermissionInfo info = p.usesPermissionInfos.get(i);
+ final String perm = info.getPermission();
pi.requestedPermissions[i] = perm;
+ int permissionFlags = 0;
// The notion of required permissions is deprecated but for compatibility.
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+ permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+ permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
+ pi.requestedPermissionsFlags[i] = permissionFlags;
+ pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags);
}
}
}
@@ -829,9 +834,6 @@
public static final int PARSE_MUST_BE_APK = 1 << 0;
public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
- /** @deprecated forward lock no longer functional. remove. */
- @Deprecated
- public static final int PARSE_FORWARD_LOCK = 1 << 2;
public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
@@ -845,7 +847,6 @@
PARSE_ENFORCE_CODE,
PARSE_EXTERNAL_STORAGE,
PARSE_FORCE_SDK,
- PARSE_FORWARD_LOCK,
PARSE_IGNORE_PROCESSES,
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
@@ -2006,11 +2007,6 @@
PARSE_DEFAULT_TARGET_SANDBOX);
pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
- /* Set the global "forward lock" flag */
- if ((flags & PARSE_FORWARD_LOCK) != 0) {
- pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
- }
-
/* Set the global "on SD card" flag */
if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -2123,12 +2119,12 @@
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
- if (!parseUsesPermission(pkg, res, parser)) {
+ if (!parseUsesPermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
- if (!parseUsesPermission(pkg, res, parser)) {
+ if (!parseUsesPermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
@@ -2451,7 +2447,7 @@
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
- pkg.requestedPermissions.add(npi.name);
+ addRequestedPermission(pkg, npi.name);
pkg.implicitPermissions.add(npi.name);
}
}
@@ -2472,7 +2468,7 @@
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!pkg.requestedPermissions.contains(perm)) {
- pkg.requestedPermissions.add(perm);
+ addRequestedPermission(pkg, perm);
pkg.implicitPermissions.add(perm);
}
}
@@ -2517,7 +2513,7 @@
// If the storage model feature flag is disabled, we need to fiddle
// around with permission definitions to return us to pre-Q behavior.
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
if ("android".equals(pkg.packageName)) {
final ArraySet<String> newGroups = new ArraySet<>();
newGroups.add(android.Manifest.permission_group.MEDIA_AURAL);
@@ -2532,55 +2528,33 @@
final ArraySet<String> newPermissions = new ArraySet<>();
newPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
newPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
newPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
newPermissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION);
newPermissions.add(android.Manifest.permission.WRITE_OBB);
- final ArraySet<String> dangerousPermissions = new ArraySet<>();
- dangerousPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
- dangerousPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ final ArraySet<String> removedPermissions = new ArraySet<>();
+ removedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
+ removedPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
for (int i = pkg.permissions.size() - 1; i >= 0; i--) {
final Permission p = pkg.permissions.get(i);
if (newPermissions.contains(p.info.name)) {
pkg.permissions.remove(i);
- } else if (dangerousPermissions.contains(p.info.name)) {
- p.info.protectionLevel &= ~PermissionInfo.PROTECTION_MASK_BASE;
- p.info.protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+ } else if (removedPermissions.contains(p.info.name)) {
+ p.info.flags &= ~PermissionInfo.FLAG_REMOVED;
}
}
}
} else {
if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO);
}
if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO);
}
if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
- }
-
- if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_LEGACY, false)) {
- if (pkg.requestedPermissions
- .contains(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- }
- if (pkg.requestedPermissions
- .contains(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
- }
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES);
}
}
@@ -2620,6 +2594,14 @@
}
/**
+ * Helper method for adding a requested permission to a package outside of a uses-permission.
+ */
+ private void addRequestedPermission(Package pkg, String permission) {
+ pkg.requestedPermissions.add(permission);
+ pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission));
+ }
+
+ /**
* Computes the targetSdkVersion to use at runtime. If the package is not
* compatible with this platform, populates {@code outError[0]} with an
* error message.
@@ -2876,8 +2858,8 @@
return certSha256Digests;
}
- private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
- throws XmlPullParserException, IOException {
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
+ String[] outError) throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
@@ -2901,6 +2883,44 @@
final String requiredNotfeature = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
+ int dataSentOffDevice = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0);
+
+ int dataSharedWithThirdParty = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0);
+
+ int dataUsedForMonetization = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0);
+
+ int retentionWeeks = -1;
+ int retention;
+
+ String rawRetention = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime);
+
+ if (rawRetention == null) {
+ retention = UsesPermissionInfo.RETENTION_UNDEFINED;
+ } else if ("notRetained".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_NOT_RETAINED;
+ } else if ("userSelected".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_USER_SELECTED;
+ } else if ("unlimited".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_UNLIMITED;
+ } else {
+ // A number of weeks was specified
+ retention = UsesPermissionInfo.RETENTION_SPECIFIED;
+ retentionWeeks = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime,
+ -1);
+
+ if (retentionWeeks < 0) {
+ outError[0] = "Bad value provided for dataRetentionTime.";
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ XmlUtils.skipCurrentTag(parser);
+ sa.recycle();
+ return false;
+ }
+ }
sa.recycle();
XmlUtils.skipCurrentTag(parser);
@@ -2933,6 +2953,10 @@
+ parser.getPositionDescription());
}
+ UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice,
+ dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks);
+ pkg.usesPermissionInfos.add(info);
+
return true;
}
@@ -3267,6 +3291,10 @@
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
+ perm.info.usageInfoRequired = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0)
+ != 0;
+
sa.recycle();
if (perm.info.protectionLevel == -1) {
@@ -5369,6 +5397,11 @@
s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
}
if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestService_useAppZygote,
+ false)) {
+ s.info.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
+ }
+ if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestService_singleUser,
false)) {
s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
@@ -6396,6 +6429,9 @@
@UnsupportedAppUsage
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+ public final ArrayList<UsesPermissionInfo> usesPermissionInfos =
+ new ArrayList<>();
+
/** Permissions requested but not in the manifest. */
public final ArrayList<String> implicitPermissions = new ArrayList<>();
@@ -6801,7 +6837,7 @@
/** @hide */
public boolean isForwardLocked() {
- return applicationInfo.isForwardLocked();
+ return false;
}
/** @hide */
@@ -6843,9 +6879,7 @@
public boolean canHaveOatDir() {
// The following app types CANNOT have oat directory
// - non-updated system apps
- // - forward-locked apps or apps installed in ASEC containers
- return (!isSystem() || isUpdatedSystemApp())
- && !isForwardLocked() && !applicationInfo.isExternalAsec();
+ return !isSystem() || isUpdatedSystemApp();
}
public boolean isMatch(int flags) {
@@ -6928,6 +6962,7 @@
dest.readStringList(requestedPermissions);
internStringArrayList(requestedPermissions);
+ dest.readParcelableList(usesPermissionInfos, boot);
dest.readStringList(implicitPermissions);
internStringArrayList(implicitPermissions);
protectedBroadcasts = dest.createStringArrayList();
@@ -7094,6 +7129,7 @@
dest.writeParcelableList(instrumentation, flags);
dest.writeStringList(requestedPermissions);
+ dest.writeParcelableList(usesPermissionInfos, flags);
dest.writeStringList(implicitPermissions);
dest.writeStringList(protectedBroadcasts);
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index e21c33a..be6ed51 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -21,7 +21,6 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -130,9 +129,6 @@
* </p>
*/
public boolean isMatch(ComponentInfo componentInfo, int flags) {
- if ((flags & MATCH_ALL) != 0) {
- return true;
- }
final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
if (!isAvailable(flags)
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 60c06a1..d9d6b5f 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -308,6 +309,12 @@
*/
public CharSequence nonLocalizedDescription;
+ /**
+ * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em>
+ * include permission data usage information in order to be able to be granted this permission.
+ */
+ public boolean usageInfoRequired;
+
/** @hide */
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -394,6 +401,7 @@
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
+ usageInfoRequired = orig.usageInfoRequired;
}
/**
@@ -458,6 +466,7 @@
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+ dest.writeInt(usageInfoRequired ? 1 : 0);
}
/** @hide */
@@ -498,5 +507,6 @@
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ usageInfoRequired = source.readInt() != 0;
}
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 91f884c..ad2c72274 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -56,6 +56,23 @@
public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
/**
+ * Bit in {@link #flags}: If set, the service (which must be isolated)
+ * will be spawned from an Application Zygote, instead of the regular Zygote.
+ * The Application Zygote will pre-initialize the application's class loader,
+ * and call a static callback into the application to allow it to perform
+ * application-specific preloads (such as loading a shared library). Therefore,
+ * spawning from the Application Zygote will typically reduce the service
+ * launch time and reduce its memory usage. The downside of using this flag
+ * is that you will have an additional process (the app zygote itself) that
+ * is taking up memory. Whether actual memory usage is improved therefore
+ * strongly depends on the number of isolated services that an application
+ * starts, and how much memory those services save by preloading. Therefore,
+ * it is recommended to measure memory usage under typical workloads to
+ * determine whether it makes sense to use this flag.
+ */
+ public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
+
+ /**
* Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
* @hide
*/
diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java
new file mode 100644
index 0000000..d08548f
--- /dev/null
+++ b/core/java/android/content/pm/UsesPermissionInfo.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.RetentionPolicy;
+/**
+ * Information you can retrive about a particular application requested permission. This
+ * corresponds to information collected from the AndroidManifest.xml's <uses-permission>
+ * tags.
+ */
+public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable {
+
+ /**
+ * Flag for {@link #getFlags()}: the requested permission is currently granted to the
+ * application.
+ */
+ public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /** An unset value for {@link #getDataSentOffDevice()},
+ * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+ */
+ public static final int USAGE_UNDEFINED = 0;
+
+ /**
+ * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+ * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_YES = 1;
+
+ /**
+ * A user triggered only value for {@link #getDataSentOffDevice()},
+ * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+ * corresponding to the <code>userTriggered</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_USER_TRIGGERED = 2;
+
+ /**
+ * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+ * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_NO = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"USAGE_"}, value = {
+ USAGE_UNDEFINED,
+ USAGE_YES,
+ USAGE_USER_TRIGGERED,
+ USAGE_NO})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Usage {}
+
+ /**
+ * An unset value for {@link #getDataRetention}.
+ */
+ public static final int RETENTION_UNDEFINED = 0;
+
+ /**
+ * A data not retained value for {@link #getDataRetention()} corresponding to the
+ * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_NOT_RETAINED = 1;
+
+ /**
+ * A user selected value for {@link #getDataRetention()} corresponding to the
+ * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_USER_SELECTED = 2;
+
+ /**
+ * An unlimited value for {@link #getDataRetention()} corresponding to the
+ * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_UNLIMITED = 3;
+
+ /**
+ * A specified value for {@link #getDataRetention()} corresponding to providing the number of
+ * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks
+ * is available in {@link #getDataRetentionWeeks()}.
+ */
+ public static final int RETENTION_SPECIFIED = 4;
+
+ /** @hide */
+ @IntDef(prefix = {"RETENTION_"}, value = {
+ RETENTION_UNDEFINED,
+ RETENTION_NOT_RETAINED,
+ RETENTION_USER_SELECTED,
+ RETENTION_UNLIMITED,
+ RETENTION_SPECIFIED})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Retention {}
+
+ private final String mPermission;
+ private final @Flags int mFlags;
+ private final @Usage int mDataSentOffDevice;
+ private final @Usage int mDataSharedWithThirdParty;
+ private final @Usage int mDataUsedForMonetization;
+ private final @Retention int mDataRetention;
+ private final int mDataRetentionWeeks;
+
+ /** @hide */
+ public UsesPermissionInfo(String permission) {
+ mPermission = permission;
+ mDataSentOffDevice = USAGE_UNDEFINED;
+ mDataSharedWithThirdParty = USAGE_UNDEFINED;
+ mDataUsedForMonetization = USAGE_UNDEFINED;
+ mDataRetention = RETENTION_UNDEFINED;
+ mDataRetentionWeeks = -1;
+ mFlags = 0;
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(String permission,
+ @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty,
+ @Usage int dataUsedForMonetization, @Retention int dataRetention,
+ int dataRetentionWeeks) {
+ mPermission = permission;
+ mDataSentOffDevice = dataSentOffDevice;
+ mDataSharedWithThirdParty = dataSharedWithThirdParty;
+ mDataUsedForMonetization = dataUsedForMonetization;
+ mDataRetention = dataRetention;
+ mDataRetentionWeeks = dataRetentionWeeks;
+ mFlags = 0;
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(UsesPermissionInfo orig) {
+ this(orig, orig.mFlags);
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(UsesPermissionInfo orig, int flags) {
+ super(orig);
+ mPermission = orig.mPermission;
+ mFlags = flags;
+ mDataSentOffDevice = orig.mDataSentOffDevice;
+ mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty;
+ mDataUsedForMonetization = orig.mDataUsedForMonetization;
+ mDataRetention = orig.mDataRetention;
+ mDataRetentionWeeks = orig.mDataRetentionWeeks;
+ }
+
+ /**
+ * The name of the requested permission.
+ */
+ public String getPermission() {
+ return mPermission;
+ }
+
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * If the application sends the data guarded by this permission off the device.
+ *
+ * See {@link android.R.attr#dataSentOffDevice}
+ */
+ public @Usage int getDataSentOffDevice() {
+ return mDataSentOffDevice;
+ }
+
+ /**
+ * If the application or its services shares the data guarded by this permission with third
+ * parties.
+ *
+ * See {@link android.R.attr#dataSharedWithThirdParty}
+ */
+ public @Usage int getDataSharedWithThirdParty() {
+ return mDataSharedWithThirdParty;
+ }
+
+ /**
+ * If the application or its services use the data guarded by this permission for monetization
+ * purposes.
+ *
+ * See {@link android.R.attr#dataUsedForMonetization}
+ */
+ public @Usage int getDataUsedForMonetization() {
+ return mDataUsedForMonetization;
+ }
+
+ /**
+ * How long the application or its services store the data guarded by this permission.
+ * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the
+ * number of weeks the data is stored.
+ *
+ * See {@link android.R.attr#dataRetentionTime}
+ */
+ public @Retention int getDataRetention() {
+ return mDataRetention;
+ }
+
+ /**
+ * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the
+ * application or its services store data guarded by this permission.
+ *
+ * @throws IllegalStateException if {@link #getDataRetention} is not
+ * {@link #RETENTION_SPECIFIED}.
+ */
+ public int getDataRetentionWeeks() {
+ if (mDataRetention != RETENTION_SPECIFIED) {
+ throw new IllegalStateException("Data retention weeks not specified");
+ }
+ return mDataRetentionWeeks;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mPermission);
+ dest.writeInt(mFlags);
+ dest.writeInt(mDataSentOffDevice);
+ dest.writeInt(mDataSharedWithThirdParty);
+ dest.writeInt(mDataUsedForMonetization);
+ dest.writeInt(mDataRetention);
+ dest.writeInt(mDataRetentionWeeks);
+ }
+
+ private UsesPermissionInfo(Parcel source) {
+ super(source);
+ mPermission = source.readString();
+ mFlags = source.readInt();
+ mDataSentOffDevice = source.readInt();
+ mDataSharedWithThirdParty = source.readInt();
+ mDataUsedForMonetization = source.readInt();
+ mDataRetention = source.readInt();
+ mDataRetentionWeeks = source.readInt();
+ }
+
+ public static final Creator<UsesPermissionInfo> CREATOR =
+ new Creator<UsesPermissionInfo>() {
+ @Override
+ public UsesPermissionInfo createFromParcel(Parcel source) {
+ return new UsesPermissionInfo(source);
+ }
+ @Override
+ public UsesPermissionInfo[] newArray(int size) {
+ return new UsesPermissionInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 79bc9a3..73addb7 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -16,13 +16,15 @@
package android.content.pm.permission;
+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.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -32,7 +34,7 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayList;
import java.util.Collections;
@@ -109,13 +111,11 @@
*/
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);
+ checkNotNull(packageName);
+ checkNotNull(callback);
+
+ mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
+ mRemoteService, packageName, callback, handler));
}
/**
@@ -124,24 +124,20 @@
* @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;
+ public void revokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName) {
+ checkNotNull(packageName);
+ checkNotNull(permissionName);
- Message message = mRemoteService.obtainMessage(
- RemoteService.MSG_REVOKE_APP_PERMISSIONS, args);
- mRemoteService.processMessage(message);
+ mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
+ mRemoteService, packageName, permissionName));
}
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;
+ public static final int MSG_UNBIND = 0;
private final Object mLock = new Object();
@@ -191,82 +187,57 @@
}
}
- @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);
- }
+ private void getAppPermissions(@NonNull String packageName,
+ @NonNull OnResultCallback 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();
- } break;
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
+ }
+ scheduleUnbind();
- case MSG_UNBIND: {
- synchronized (mLock) {
- if (mBound) {
- mContext.unbindService(this);
- mBound = false;
- }
- mRemoteInstance = null;
- }
- } break;
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
- 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;
+ 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) {
@@ -274,6 +245,16 @@
}
}
+ 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()) {
@@ -284,7 +265,8 @@
private void scheduleUnbind() {
removeMessages(MSG_UNBIND);
- sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS);
+ sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
+ .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
}
}
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 4371c77..740cdae 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,7 +58,7 @@
public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
- private static final boolean FEATURE_FLAG_IDMAP2 = false;
+ private static final boolean FEATURE_FLAG_IDMAP2 = true;
private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 79e15a7a..0ec812f 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -30,19 +30,29 @@
public interface BiometricAuthenticator {
/**
+ * No biometric methods or nothing has been enrolled.
+ * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
+ * modalities when calling authenticate().
* @hide
*/
- int TYPE_FINGERPRINT = 1;
+ int TYPE_NONE = 0;
+ /**
+ * Constant representing fingerprint.
+ * @hide
+ */
+ int TYPE_FINGERPRINT = 1 << 0;
/**
+ * Constant representing iris.
* @hide
*/
- int TYPE_IRIS = 2;
+ int TYPE_IRIS = 1 << 1;
/**
+ * Constant representing face.
* @hide
*/
- int TYPE_FACE = 3;
+ int TYPE_FACE = 1 << 2;
/**
* Container for biometric data
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/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index bd149fd..b238d77 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -251,9 +251,40 @@
private Executor mExecutor;
private AuthenticationCallback mAuthenticationCallback;
- IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
+ private final IBiometricServiceReceiver mBiometricServiceReceiver =
+ new IBiometricServiceReceiver.Stub() {
+
@Override
- public void onDialogDismissed(int reason) {
+ public void onAuthenticationSucceeded() throws RemoteException {
+ mExecutor.execute(() -> {
+ final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ });
+ }
+
+ @Override
+ public void onAuthenticationFailed() throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationFailed();
+ });
+ }
+
+ @Override
+ public void onError(int error, String message) throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationError(error, message);
+ });
+ }
+
+ @Override
+ public void onAcquired(int acquireInfo, String message) throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
+ });
+ }
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
// Check the reason and invoke OnClickListener(s) if necessary
if (reason == DISMISSED_REASON_POSITIVE) {
mPositiveButtonInfo.executor.execute(() -> {
@@ -267,40 +298,6 @@
}
};
- IBiometricServiceReceiver mBiometricServiceReceiver =
- new IBiometricServiceReceiver.Stub() {
-
- @Override
- public void onAuthenticationSucceeded(long deviceId) throws RemoteException {
- mExecutor.execute(() -> {
- final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
- mAuthenticationCallback.onAuthenticationSucceeded(result);
- });
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationFailed();
- });
- }
-
- @Override
- public void onError(long deviceId, int error, String message)
- throws RemoteException {
- mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationError(error, message);
- });
- }
-
- @Override
- public void onAcquired(long deviceId, int acquireInfo, String message) {
- mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
- });
- }
- };
-
private BiometricPrompt(Context context, Bundle bundle,
ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
mContext = context;
@@ -557,9 +554,8 @@
mExecutor = executor;
mAuthenticationCallback = callback;
final long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, userId,
- mBiometricServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
- mBundle, mDialogReceiver);
+ mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
+ mContext.getOpPackageName(), mBundle);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
mExecutor.execute(() -> {
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index e17feff..de828f2 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -18,7 +18,6 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
/**
@@ -32,8 +31,7 @@
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+ IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
@@ -46,4 +44,11 @@
// Explicitly set the active user.
void setActiveUser(int userId);
+
+ // 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/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index a6e3696..22ef33e 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -16,12 +16,18 @@
package android.hardware.biometrics;
/**
- * Communication channel from the BiometricService back to BiometricPrompt.
+ * Communication channel from BiometricService back to BiometricPrompt
* @hide
*/
oneway interface IBiometricServiceReceiver {
- void onAuthenticationSucceeded(long deviceId);
- void onAuthenticationFailed(long deviceId);
- void onError(long deviceId, int error, String message);
- void onAcquired(long deviceId, int acquiredInfo, String message);
+ // Notify BiometricPrompt that authentication was successful
+ void onAuthenticationSucceeded();
+ // Noties that authentication failed.
+ void onAuthenticationFailed();
+ // Notify BiometricPrompt that an error has occurred.
+ void onError(int error, String message);
+ // Notifies that a biometric has been acquired.
+ void onAcquired(int acquiredInfo, String message);
+ // Notifies that the SystemUI dialog has been dismissed.
+ void onDialogDismissed(int reason);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
new file mode 100644
index 0000000..180daaf
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+/**
+ * Communication channel from
+ * 1) BiometricDialogImpl (SysUI) back to BiometricService
+ * 2) <Biometric>Service back to BiometricService
+ * Receives messages from the above and does some handling before forwarding to BiometricPrompt
+ * via IBiometricServiceReceiver.
+ * @hide
+ */
+oneway interface IBiometricServiceReceiverInternal {
+ // Notify BiometricService that authentication was successful. If user confirmation is required,
+ // the auth token must be submitted into KeyStore.
+ void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
+ // Notify BiometricService that an error has occurred.
+ void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+ // Notify BiometricService than an error has occured. Forward to the correct receiver depending
+ // on the cookie.
+ void onError(int cookie, int error, String message);
+ // Notifies that a biometric has been acquired.
+ void onAcquired(int acquiredInfo, String message);
+ // Notifies that the SystemUI dialog has been dismissed.
+ void onDialogDismissed(int reason);
+ // Notifies that the user has pressed the "try again" button on SystemUI
+ void onTryAgainPressed();
+}
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 9db1f92..9d61f02 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -24,7 +24,7 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
-import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
/**
* Display manager local system service interface.
@@ -126,7 +126,7 @@
* Called by the window manager to perform traversals while holding a
* surface flinger transaction.
*/
- public abstract void performTraversal(SurfaceControl.Transaction t);
+ public abstract void performTraversal(Transaction t);
/**
* Tells the display manager about properties of the display that depend on the windows on it.
@@ -383,6 +383,6 @@
* update the position of its surfaces as part of the same transaction.
*/
public interface DisplayTransactionListener {
- void onDisplayTransaction();
+ void onDisplayTransaction(Transaction t);
}
}
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index df0d46b..f2c50b5 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -19,6 +19,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.text.TextUtils;
@@ -71,6 +72,9 @@
// The ID used to uniquely identify this display.
public String uniqueId;
+ // The physical port that the associated display device is connected to.
+ public @Nullable Byte physicalPort;
+
public @ViewportType int type;
public void copyFrom(DisplayViewport viewport) {
@@ -82,6 +86,7 @@
deviceWidth = viewport.deviceWidth;
deviceHeight = viewport.deviceHeight;
uniqueId = viewport.uniqueId;
+ physicalPort = viewport.physicalPort;
type = viewport.type;
}
@@ -113,6 +118,7 @@
&& deviceWidth == other.deviceWidth
&& deviceHeight == other.deviceHeight
&& TextUtils.equals(uniqueId, other.uniqueId)
+ && physicalPort == other.physicalPort
&& type == other.type;
}
@@ -128,6 +134,7 @@
result += prime * result + deviceWidth;
result += prime * result + deviceHeight;
result += prime * result + uniqueId.hashCode();
+ result += prime * result + physicalPort;
result += prime * result + type;
return result;
}
@@ -139,6 +146,7 @@
+ ", valid=" + valid
+ ", displayId=" + displayId
+ ", uniqueId='" + uniqueId + "'"
+ + ", physicalPort=" + physicalPort
+ ", orientation=" + orientation
+ ", logicalFrame=" + logicalFrame
+ ", physicalFrame=" + physicalFrame
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/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 47df8e8..a15dcec 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -15,9 +15,7 @@
*/
package android.hardware.face;
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -32,19 +30,24 @@
void authenticate(IBinder token, long sessionId,
IFaceServiceReceiver receiver, int flags, String opPackageName);
- // This method invokes the BiometricDialog. The arguments are almost the same as above,
- // but should only be called from (BiometricPromptService).
- void authenticateFromService(boolean requireConfirmation, IBinder token, long sessionId,
- int userId, IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId);
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId);
+
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Same as above, with extra arguments.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start face enrollment
void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2662a11..dd6b29d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,9 +15,7 @@
*/
package android.hardware.fingerprint;
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -35,22 +33,25 @@
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
- // This method invokes the BiometricDialog. The arguments are almost the same as above, except
- // this is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
- // called from BiometricPromptService. The additional uid, pid, userId arguments should be
- // determined by BiometricPromptService.
- void authenticateFromService(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie,
int callingUid, int callingPid, int callingUserId);
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
+
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 539c494..d1190ab 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Intent;
@@ -30,6 +31,7 @@
*
* @hide
*/
+@SystemApi
public class ContextHubIntentEvent {
@ContextHubManager.Event private final int mEventType;
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 88fb3de..7639302 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -56,38 +56,28 @@
/**
* An extra of type {@link ContextHubInfo} describing the source of the event.
- *
- * @hide
*/
public static final String EXTRA_CONTEXT_HUB_INFO =
"android.hardware.location.extra.CONTEXT_HUB_INFO";
/**
* An extra of type {@link ContextHubManager.Event} describing the event type.
- *
- * @hide
*/
public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
/**
* An extra of type long describing the ID of the nanoapp an event is for.
- *
- * @hide
*/
public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
/**
* An extra of type int describing the nanoapp-specific abort code.
- *
- * @hide
*/
public static final String EXTRA_NANOAPP_ABORT_CODE =
"android.hardware.location.extra.NANOAPP_ABORT_CODE";
/**
* An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
- *
- * @hide
*/
public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
@@ -109,56 +99,41 @@
/**
* An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_LOADED = 0;
/**
* An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_UNLOADED = 1;
/**
* An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_ENABLED = 2;
/**
* An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_DISABLED = 3;
/**
* An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and
* EXTRA_NANOAPP_ABORT_CODE extras.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_ABORTED = 4;
/**
* An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and
* EXTRA_NANOAPP_MESSAGE extras.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_MESSAGE = 5;
/**
* An event describing that the Context Hub has reset.
- *
- * @hide
*/
public static final int EVENT_HUB_RESET = 6;
-
private final Looper mMainLooper;
private final IContextHubService mService;
private Callback mCallback;
@@ -797,14 +772,14 @@
* Creates a ContextHubClient that will receive notifications based on Intent events.
*
* This method should be used instead of {@link #createClient(ContextHubInfo,
- * ContextHubClientCallback)} and the equivalent API if the caller wants to preserve the
- * messaging endpoint of a ContextHubClient, even after a process exits. If the PendingIntent
- * with the provided nanoapp has already been registered at the service previously, then the
- * same ContextHubClient will be regenerated without creating a new client connection at the
- * service. Note that the PendingIntent, nanoapp, and Context Hub must all match in identifying
- * a previously registered ContextHubClient. If a client is regenerated, it can be treated as
- * the same endpoint entity from a nanoapp's perspective, and can be continued to be
- * used to send messages even if the original process has exited.
+ * ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback,
+ * Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even
+ * after a process exits. If the PendingIntent with the provided nanoapp has already been
+ * registered at the service, then the same ContextHubClient will be regenerated without
+ * creating a new client connection at the service. Note that the PendingIntent, nanoapp, and
+ * Context Hub must all match in identifying a previously registered ContextHubClient.
+ * If a client is regenerated, the host endpoint identifier attached to messages sent to the
+ * nanoapp remains consistent, even if the original process has exited.
*
* If registered successfully, intents will be delivered regarding events or messages from the
* specified nanoapp from the attached Context Hub. The intent will have an extra
@@ -815,10 +790,11 @@
* each event type, along with event-specific extra fields. The client can also use
* {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
*
- * Intent events will be delivered until it is unregistered through
- * {@link ContextHubClient.close()}. Note that the registration of this
- * ContextHubClient at the Context Hub Service will continued to be maintained until
- * {@link ContextHubClient.close()} is called.
+ * Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that
+ * the registration of this ContextHubClient at the Context Hub Service will be maintained until
+ * {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called
+ * on the provided PendingIntent, then the client will be automatically unregistered by the
+ * service.
*
* @param hubInfo the hub to attach this client to
* @param pendingIntent the PendingIntent to register to the client
@@ -828,8 +804,6 @@
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
* @throws NullPointerException if pendingIntent or hubInfo is null
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
@NonNull public ContextHubClient createClient(
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 8599f47..3552655 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -65,10 +65,13 @@
// An interval, in seconds between the NattKeepalive packets
private int mNattKeepaliveInterval;
- // XFRM mark and mask
+ // XFRM mark and mask; defaults to 0 (no mark/mask)
private int mMarkValue;
private int mMarkMask;
+ // XFRM interface id
+ private int mXfrmInterfaceId;
+
/** Set the mode for this IPsec transform */
public void setMode(int mode) {
mMode = mode;
@@ -125,14 +128,30 @@
mNattKeepaliveInterval = interval;
}
+ /**
+ * Sets the mark value
+ *
+ * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+ * ignored.
+ */
public void setMarkValue(int mark) {
mMarkValue = mark;
}
+ /**
+ * Sets the mark mask
+ *
+ * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+ * ignored.
+ */
public void setMarkMask(int mask) {
mMarkMask = mask;
}
+ public void setXfrmInterfaceId(int xfrmInterfaceId) {
+ mXfrmInterfaceId = xfrmInterfaceId;
+ }
+
// Transport or Tunnel
public int getMode() {
return mMode;
@@ -190,6 +209,10 @@
return mMarkMask;
}
+ public int getXfrmInterfaceId() {
+ return mXfrmInterfaceId;
+ }
+
// Parcelable Methods
@Override
@@ -213,6 +236,7 @@
out.writeInt(mNattKeepaliveInterval);
out.writeInt(mMarkValue);
out.writeInt(mMarkMask);
+ out.writeInt(mXfrmInterfaceId);
}
@VisibleForTesting
@@ -235,6 +259,7 @@
mNattKeepaliveInterval = c.mNattKeepaliveInterval;
mMarkValue = c.mMarkValue;
mMarkMask = c.mMarkMask;
+ mXfrmInterfaceId = c.mXfrmInterfaceId;
}
private IpSecConfig(Parcel in) {
@@ -255,6 +280,7 @@
mNattKeepaliveInterval = in.readInt();
mMarkValue = in.readInt();
mMarkMask = in.readInt();
+ mXfrmInterfaceId = in.readInt();
}
@Override
@@ -289,6 +315,8 @@
.append(mMarkValue)
.append(", mMarkMask=")
.append(mMarkMask)
+ .append(", mXfrmInterfaceId=")
+ .append(mXfrmInterfaceId)
.append("}");
return strBuilder.toString();
@@ -320,10 +348,10 @@
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
&& lhs.mSpiResourceId == rhs.mSpiResourceId
&& IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
- && IpSecAlgorithm.equals(
- lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+ && IpSecAlgorithm.equals(lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
&& IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)
&& lhs.mMarkValue == rhs.mMarkValue
- && lhs.mMarkMask == rhs.mMarkMask);
+ && lhs.mMarkMask == rhs.mMarkMask
+ && lhs.mXfrmInterfaceId == rhs.mXfrmInterfaceId);
}
}
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 1999e78..cb82fbe 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -44,6 +44,8 @@
public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10;
public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11;
+ public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12;
+
@IntDef(value = {
NETWORK_CONNECTED,
NETWORK_VALIDATED,
@@ -56,6 +58,7 @@
NETWORK_REVALIDATION_SUCCESS,
NETWORK_FIRST_VALIDATION_PORTAL_FOUND,
NETWORK_REVALIDATION_PORTAL_FOUND,
+ NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index c7184c0..2ae796c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -85,6 +85,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;
@@ -400,6 +409,9 @@
* }
* </pre>
*
+ * <p>The work source will be propagated for future outgoing binder transactions
+ * executed on this thread.
+ *
* @param workSource The original UID responsible for the binder call.
* @return token to restore original work source.
* @hide
@@ -423,6 +435,9 @@
/**
* Clears the work source on this thread.
*
+ * <p>The work source will be propagated for future outgoing binder transactions
+ * executed on this thread.
+ *
* @return token to restore original work source.
* @hide
**/
@@ -442,6 +457,7 @@
* Binder.restoreCallingWorkSource(token);
* }
* </pre>
+ *
* @hide
**/
@CriticalNative
@@ -903,6 +919,16 @@
// Entry point from android_util_Binder.cpp's onTransact
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
+ final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
+ try {
+ return execTransactInternal(code, dataObj, replyObj, flags);
+ } finally {
+ ThreadLocalWorkSource.restore(origWorkSource);
+ }
+ }
+
+ private boolean execTransactInternal(int code, long dataObj, long replyObj,
+ int flags) {
// Make sure the observer won't change while processing a transaction.
final BinderInternal.Observer observer = sObserver;
final CallSession callSession =
@@ -916,10 +942,11 @@
// 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) {
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
+ final String transactionName = getTransactionName(code);
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
+ + (transactionName != null ? transactionName : code));
}
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
@@ -941,7 +968,6 @@
}
res = true;
} finally {
- ThreadLocalWorkSource.restore(origWorkSource);
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
@@ -961,7 +987,6 @@
if (observer != null) {
observer.callEnded(callSession, requestSizeBytes, replySizeBytes);
}
-
return res;
}
}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 720c167..97d72f0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -360,6 +360,16 @@
}
/**
+ * Returns the number of binder proxies held in this process.
+ * @return number of binder proxies in this process
+ */
+ public static int getProxyCount() {
+ synchronized (sProxyMap) {
+ return sProxyMap.size();
+ }
+ }
+
+ /**
* Dump proxy debug information.
*
* @hide
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 7858c59..767c15c 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -83,6 +83,14 @@
= "android.intent.action.UPDATE_SMART_SELECTION";
/**
+ * Update conversation actions model file.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_CONVERSATION_ACTIONS
+ = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
+
+ /**
* Update network watchlist config file.
* @hide
*/
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/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8c5c415..8cafbde 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -54,7 +54,7 @@
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
- private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+ private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
@@ -269,7 +269,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;
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/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 44b9e311..6de1ff4 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,12 +17,6 @@
package android.os;
import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.O_APPEND;
-import static android.system.OsConstants.O_CREAT;
-import static android.system.OsConstants.O_RDONLY;
-import static android.system.OsConstants.O_RDWR;
-import static android.system.OsConstants.O_TRUNC;
-import static android.system.OsConstants.O_WRONLY;
import static android.system.OsConstants.SEEK_SET;
import static android.system.OsConstants.SOCK_SEQPACKET;
import static android.system.OsConstants.SOCK_STREAM;
@@ -254,8 +248,16 @@
}
/** {@hide} */
- public static ParcelFileDescriptor fromFd(
- FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException {
+ public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler,
+ final OnCloseListener listener) throws IOException {
+ final FileDescriptor original = new FileDescriptor();
+ original.setInt$(pfd.detachFd());
+ return fromFd(original, handler, listener);
+ }
+
+ /** {@hide} */
+ public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler,
+ final OnCloseListener listener) throws IOException {
if (handler == null) {
throw new IllegalArgumentException("Handler must not be null");
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1c1db68..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;
/**
@@ -1008,7 +1017,8 @@
* progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when
* dream ends.
* </p><p>
- * Requires the {@link android.Manifest.permission#WRITE_DREAM_STATE} permission.
+ * Requires the {@link android.Manifest.permission#READ_DREAM_STATE} and
+ * {@link android.Manifest.permission#WRITE_DREAM_STATE} permissions.
* </p>
*
* @param time The time when the request to nap was issued, in the
@@ -1019,7 +1029,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.READ_DREAM_STATE,
+ android.Manifest.permission.WRITE_DREAM_STATE })
public void dream(long time) {
Sandman.startDreamByUserRequest(mContext);
}
@@ -1440,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/Process.java b/core/java/android/os/Process.java
index 651caec..2abcb4c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1055,6 +1055,9 @@
*/
public static final native long getPss(int pid);
+ /** @hide */
+ public static final native long[] getRss(int pid);
+
/**
* Specifies the outcome of having started a process.
* @hide
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
index 60eb5c3..4e5eaac 100644
--- a/core/java/android/os/RedactingFileDescriptor.java
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -20,15 +20,18 @@
import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.OsConstants;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.util.Arrays;
/**
* Variant of {@link FileDescriptor} that allows its creator to specify regions
@@ -40,20 +43,21 @@
private static final String TAG = "RedactingFileDescriptor";
private static final boolean DEBUG = true;
- private final long[] mRedactRanges;
+ private volatile long[] mRedactRanges;
private FileDescriptor mInner = null;
private ParcelFileDescriptor mOuter = null;
- private RedactingFileDescriptor(Context context, File file, long[] redactRanges)
+ private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges)
throws IOException {
mRedactRanges = checkRangesArgument(redactRanges);
try {
try {
- mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0);
+ mInner = Os.open(file.getAbsolutePath(),
+ FileUtils.translateModePfdToPosix(mode), 0);
mOuter = context.getSystemService(StorageManager.class)
- .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback);
+ .openProxyFileDescriptor(mode, mCallback);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
@@ -78,16 +82,61 @@
/**
* Open the given {@link File} and returns a {@link ParcelFileDescriptor}
- * that offers a redacted, read-only view of the underlying data.
+ * that offers a redacted view of the underlying data. If a redacted region
+ * is written to, the newly written data can be read back correctly instead
+ * of continuing to be redacted.
*
* @param file The underlying file to open.
+ * @param mode The {@link ParcelFileDescriptor} mode to open with.
* @param redactRanges List of file offsets that should be redacted, stored
* as {@code [start1, end1, start2, end2, ...]}. Start values are
* inclusive and end values are exclusive.
*/
- public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges)
- throws IOException {
- return new RedactingFileDescriptor(context, file, redactRanges).mOuter;
+ public static ParcelFileDescriptor open(Context context, File file, int mode,
+ long[] redactRanges) throws IOException {
+ return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter;
+ }
+
+ /**
+ * Update the given ranges argument to remove any references to the given
+ * offset and length. This is typically used when a caller has written over
+ * a previously redacted region.
+ */
+ @VisibleForTesting
+ public static long[] removeRange(long[] ranges, long start, long end) {
+ if (start == end) {
+ return ranges;
+ } else if (start > end) {
+ throw new IllegalArgumentException();
+ }
+
+ long[] res = EmptyArray.LONG;
+ for (int i = 0; i < ranges.length; i += 2) {
+ if (start <= ranges[i] && end >= ranges[i + 1]) {
+ // Range entirely covered; remove it
+ } else if (start >= ranges[i] && end <= ranges[i + 1]) {
+ // Range partially covered; punch a hole
+ res = Arrays.copyOf(res, res.length + 4);
+ res[res.length - 4] = ranges[i];
+ res[res.length - 3] = start;
+ res[res.length - 2] = end;
+ res[res.length - 1] = ranges[i + 1];
+ } else {
+ // Range might covered; adjust edges if needed
+ res = Arrays.copyOf(res, res.length + 2);
+ if (end >= ranges[i] && end <= ranges[i + 1]) {
+ res[res.length - 2] = Math.max(ranges[i], end);
+ } else {
+ res[res.length - 2] = ranges[i];
+ }
+ if (start >= ranges[i] && start <= ranges[i + 1]) {
+ res[res.length - 1] = Math.min(ranges[i + 1], start);
+ } else {
+ res[res.length - 1] = ranges[i + 1];
+ }
+ }
+ }
+ return res;
}
private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
@@ -126,7 +175,24 @@
@Override
public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
- throw new ErrnoException(TAG, OsConstants.EBADF);
+ int n = 0;
+ while (n < size) {
+ try {
+ final int res = Os.pwrite(mInner, data, n, size - n, offset + n);
+ if (res == 0) {
+ break;
+ } else {
+ n += res;
+ }
+ } catch (InterruptedIOException e) {
+ n += e.bytesTransferred;
+ }
+ }
+
+ // Clear any relevant redaction ranges before returning, since the
+ // writer should have access to see the data they just overwrote
+ mRedactRanges = removeRange(mRedactRanges, offset, offset + n);
+ return n;
}
@Override
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 866bd9a..acb9eac 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -43,6 +43,7 @@
int mTag;
long mElapsedTimeNs;
long mWallClockTimeNs;
+ WorkSource mWorkSource = null;
public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
this.mTag = tag;
@@ -71,6 +72,17 @@
};
/**
+ * Set work source if any.
+ */
+ public void setWorkSource(WorkSource ws) {
+ if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
+ Slog.w(TAG, "Empty worksource!");
+ return;
+ }
+ mWorkSource = ws;
+ }
+
+ /**
* Write a int value.
*/
public void writeInt(int val) {
@@ -119,11 +131,6 @@
mValues.add(val ? 1 : 0);
}
- /**
- * Writes the stored fields to a byte array. Will first write a new-line character to denote
- * END_LIST before writing contents to byte array.
- */
-
public void writeToParcel(Parcel out, int flags) {
if (DEBUG) {
Slog.d(TAG,
@@ -133,6 +140,34 @@
out.writeInt(mTag);
out.writeLong(mElapsedTimeNs);
out.writeLong(mWallClockTimeNs);
+ if (mWorkSource != null) {
+ ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
+ // number of chains
+ out.writeInt(workChains.size());
+ for (int i = 0; i < workChains.size(); i++) {
+ android.os.WorkSource.WorkChain wc = workChains.get(i);
+ if (wc.getSize() == 0) {
+ Slog.w(TAG, "Empty work chain.");
+ out.writeInt(0);
+ continue;
+ }
+ if (wc.getUids().length != wc.getTags().length
+ || wc.getUids().length != wc.getSize()) {
+ Slog.w(TAG, "Malformated work chain.");
+ out.writeInt(0);
+ continue;
+ }
+ // number of nodes
+ out.writeInt(wc.getSize());
+ for (int j = 0; j < wc.getSize(); j++) {
+ out.writeInt(wc.getUids()[j]);
+ out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
+ }
+ }
+ } else {
+ // no chains
+ out.writeInt(0);
+ }
out.writeInt(mTypes.size());
for (int i = 0; i < mTypes.size(); i++) {
out.writeInt(mTypes.get(i));
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index bf85fbd..5499181 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 = {
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/UserManager.java b/core/java/android/os/UserManager.java
index 92b3169..86f81d8 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.
@@ -2463,6 +2463,27 @@
}
/**
+ * Get the parent of a user profile.
+ *
+ * @param user the handle of the user profile
+ *
+ * @return the parent of the user or {@code null} if the user is not profile
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
+ UserInfo info = getProfileParent(user.getIdentifier());
+
+ if (info == null) {
+ return null;
+ }
+
+ return UserHandle.of(info.id);
+ }
+
+ /**
* Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
* managed profile don't run, generate notifications, or consume data or battery.
* <p>
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 423ce77..d315383 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -25,6 +25,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -55,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;
@@ -137,8 +139,6 @@
public static final String PROP_FORCE_VIDEO = "persist.fw.force_video";
/** {@hide} */
public static final String PROP_FORCE_IMAGES = "persist.fw.force_images";
- /** {@hide} */
- public static final String PROP_FORCE_LEGACY = "persist.fw.force_legacy";
/** {@hide} */
public static final String UUID_PRIVATE_INTERNAL = null;
@@ -1496,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);
}
@@ -1512,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);
}
@@ -1535,6 +1535,12 @@
return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
}
+ /** {@hide} */
+ @TestApi
+ public static boolean hasIsolatedStorage() {
+ return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+ }
+
/**
* @deprecated disabled now that FUSE has been replaced by sdcardfs
* @hide
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 18aea03..a41a644 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;
@@ -27,12 +30,8 @@
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;
/**
@@ -63,7 +62,7 @@
@Override
public final void attachBaseContext(Context base) {
super.attachBaseContext(base);
- mHandler = new MyHandler(base.getMainLooper());
+ mHandler = new Handler(base.getMainLooper());
}
/**
@@ -71,7 +70,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 +87,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(RuntimePermissionPresenter.KEY_RESULT, permissions);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
}
}
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 16d454d..4737577 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
@@ -97,8 +98,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 +119,6 @@
*
* @see DocumentsProvider#querySearchDocuments(String, String[],
* Bundle)
- * {@hide}
*/
public static final String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
@@ -124,7 +131,6 @@
*
* @see DocumentsProvider#querySearchDocuments(String, String[],
* Bundle)
- * {@hide}
*/
public static final String QUERY_ARG_MIME_TYPES = "android:query-arg-mime-types";
@@ -134,7 +140,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 +151,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,10 +219,18 @@
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";
@@ -224,10 +246,19 @@
/** {@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(ContentResolver, Uri)
+ */
+ public static final String METADATA_TYPES = "android:documentMetadataTypes";
- /** {@hide} */
+ /**
+ * Get Exif information using DocumentsContract#getDocumentMetadata.
+ *
+ * @see #getDocumentMetadata(ContentResolver, Uri)
+ */
public static final String METADATA_EXIF = "android:documentExif";
/**
@@ -487,16 +518,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(ContentResolver, Uri)
*/
- public static final int FLAG_SUPPORTS_METADATA = 1 << 17;
+ public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
}
/**
@@ -668,44 +700,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;
}
/**
@@ -1017,6 +1051,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 +1067,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 +1113,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 +1122,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 +1219,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);
}
@@ -1232,7 +1278,7 @@
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_DEFAULT);
+ ImageDecoder.ALLOCATOR_SOFTWARE);
}
/**
@@ -1270,6 +1316,31 @@
return out.getParcelable(DocumentsContract.EXTRA_URI);
}
+
+ /**
+ * 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(ContentResolver resolver, Uri parentDocumentUri,
+ Uri childDocumentUri) throws FileNotFoundException {
+ final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+ parentDocumentUri.getAuthority());
+ try {
+ return isChildDocument(client, parentDocumentUri, childDocumentUri);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to query isChildDocument", e);
+ rethrowIfNecessary(resolver, e);
+ return false;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+
/** {@hide} */
public static boolean isChildDocument(ContentProviderClient client, Uri parentDocumentUri,
Uri childDocumentUri) throws RemoteException {
@@ -1280,7 +1351,7 @@
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.");
+ throw new RemoteException("Failed to get a response from isChildDocument query.");
}
if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
throw new RemoteException("Response did not include result field..");
@@ -1496,13 +1567,13 @@
/**
* 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,7 +1593,6 @@
*
* @param documentUri a Document URI
* @return a Bundle of Bundles.
- * {@hide}
*/
public static Bundle getDocumentMetadata(ContentResolver resolver, Uri documentUri)
throws FileNotFoundException {
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/FontsContract.java b/core/java/android/provider/FontsContract.java
index 76607e9..8e37559 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -660,7 +660,22 @@
if (familyBuilder == null) {
return null;
}
- return new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+
+ final FontFamily family = familyBuilder.build();
+
+ final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
+ FontStyle.FONT_SLANT_UPRIGHT);
+ Font bestFont = family.getFont(0);
+ int bestScore = normal.getMatchScore(bestFont.getStyle());
+ for (int i = 1; i < family.getSize(); ++i) {
+ final Font candidate = family.getFont(i);
+ final int score = normal.getMatchScore(candidate.getStyle());
+ if (score < bestScore) {
+ bestFont = candidate;
+ bestScore = score;
+ }
+ }
+ return new Typeface.CustomFallbackBuilder(family).setStyle(bestFont.getStyle()).build();
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 291891e..0299e41 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -37,6 +37,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;
@@ -121,6 +122,8 @@
public static final String PARAM_INCLUDE_PENDING = "includePending";
/** {@hide} */
public static final String PARAM_PROGRESS = "progress";
+ /** {@hide} */
+ public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
/**
* Activity Action: Launch a music player.
@@ -478,6 +481,24 @@
}
/**
+ * Update the given {@link Uri} to indicate that the caller requires the
+ * original file contents when calling
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+ * <p>
+ * This can be useful when the caller wants to ensure they're backing up the
+ * exact bytes of the underlying media, without any Exif redaction being
+ * performed.
+ * <p>
+ * If the original file contents cannot be provided, a
+ * {@link UnsupportedOperationException} will be thrown when the returned
+ * {@link Uri} is used, such as when the caller doesn't hold
+ * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}.
+ */
+ public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) {
+ return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build();
+ }
+
+ /**
* Create a new pending media item using the given parameters. Pending items
* are expected to have a short lifetime, and owners should either
* {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
@@ -572,6 +593,34 @@
public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
this.secondaryDirectory = secondaryDirectory;
}
+
+ /**
+ * Optionally set the Uri from where the file has been downloaded. This is used
+ * for files being added to {@link Downloads} table.
+ *
+ * @see DownloadColumns#DOWNLOAD_URI
+ */
+ public void setDownloadUri(@Nullable Uri downloadUri) {
+ if (downloadUri == null) {
+ this.insertValues.remove(DownloadColumns.DOWNLOAD_URI);
+ } else {
+ this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
+ }
+ }
+
+ /**
+ * Optionally set the Uri indicating HTTP referer of the file. This is used for
+ * files being added to {@link Downloads} table.
+ *
+ * @see DownloadColumns#REFERER_URI
+ */
+ public void setRefererUri(@Nullable Uri refererUri) {
+ if (refererUri == null) {
+ this.insertValues.remove(DownloadColumns.REFERER_URI);
+ } else {
+ this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
+ }
+ }
}
/**
@@ -763,7 +812,7 @@
* Type: BOOLEAN
*
* @see MediaStore#createPending(Context, PendingParams)
- * @see MediaStore#QUERY_ARG_INCLUDE_PENDING
+ * @see MediaStore#PARAM_INCLUDE_PENDING
*/
public static final String IS_PENDING = "is_pending";
@@ -927,6 +976,12 @@
* Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
*/
public static final int MEDIA_TYPE_PLAYLIST = 4;
+
+ /**
+ * Column indicating if the file is part of Downloads collection.
+ * @hide
+ */
+ public static final String IS_DOWNLOAD = "is_download";
}
}
@@ -940,6 +995,80 @@
public static final Point MICRO_SIZE = new Point(96, 96);
}
+ /** Column fields for downloaded files used in {@link Downloads} table */
+ public interface DownloadColumns extends MediaColumns {
+ /**
+ * Uri indicating where the file has been downloaded from.
+ * <p>
+ * Type: TEXT
+ */
+ String DOWNLOAD_URI = "download_uri";
+
+ /**
+ * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
+ * <p>
+ * Type: TEXT
+ */
+ String REFERER_URI = "referer_uri";
+ }
+
+ /**
+ * Container for downloaded files.
+ *
+ * <p>
+ * Querying for downloads from this table will return files contributed via
+ * {@link PendingSession} and also ones which were downloaded using
+ * {@link android.app.DownloadManager} APIs.
+ */
+ public static final class Downloads implements DownloadColumns {
+ private Downloads() {}
+
+ /**
+ * The content:// style URI for the internal storage.
+ */
+ public static final Uri INTERNAL_CONTENT_URI =
+ getContentUri("internal");
+
+ /**
+ * The content:// style URI for the "primary" external storage
+ * volume.
+ */
+ public static final Uri EXTERNAL_CONTENT_URI =
+ getContentUri("external");
+
+ /**
+ * Get the content:// style URI for the downloads table on the
+ * given volume.
+ *
+ * @param volumeName the name of the volume to get the URI for
+ * @return the URI to the image media table on the given volume
+ */
+ public static Uri getContentUri(String volumeName) {
+ return AUTHORITY_URI.buildUpon().appendPath(volumeName)
+ .appendPath("downloads").build();
+ }
+
+ /** @hide */
+ public static Uri getContentUriForPath(@NonNull String path) {
+ return getContentUri(getVolumeNameForPath(path));
+ }
+ }
+
+ private static String getVolumeNameForPath(@NonNull String path) {
+ final StorageManager sm = AppGlobals.getInitialApplication()
+ .getSystemService(StorageManager.class);
+ final StorageVolume sv = sm.getStorageVolume(new File(path));
+ if (sv != null) {
+ if (sv.isPrimary()) {
+ return VOLUME_EXTERNAL;
+ } else {
+ return sv.getUuid();
+ }
+ } else {
+ return VOLUME_INTERNAL;
+ }
+ }
+
/**
* This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
* to be accessed elsewhere.
@@ -1038,13 +1167,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";
/**
@@ -1565,6 +1706,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.
@@ -1671,18 +1818,7 @@
* access this path.
*/
public static @Nullable Uri getContentUriForPath(@NonNull String path) {
- final StorageManager sm = AppGlobals.getInitialApplication()
- .getSystemService(StorageManager.class);
- final StorageVolume sv = sm.getStorageVolume(new File(path));
- if (sv != null) {
- if (sv.isPrimary()) {
- return EXTERNAL_CONTENT_URI;
- } else {
- return getContentUri(sv.getUuid());
- }
- } else {
- return INTERNAL_CONTENT_URI;
- }
+ return getContentUri(getVolumeNameForPath(path));
}
/**
@@ -2287,13 +2423,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";
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 943888b..f86296b8 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>
@@ -4252,6 +4263,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);
@@ -5075,6 +5087,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static void resetToDefaults(@NonNull ContentResolver resolver,
@Nullable String tag) {
@@ -6278,6 +6291,7 @@
*
* @hide
*/
+ @SystemApi
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
@@ -7967,6 +7981,14 @@
"managed_profile_contact_remote_search";
/**
+ * Whether parent profile can access remote calendar data in managed profile.
+ *
+ * @hide
+ */
+ public static final String CROSS_PROFILE_CALENDAR_ENABLED =
+ "cross_profile_calendar_enabled";
+
+ /**
* Whether or not the automatic storage manager is enabled and should run on the device.
*
* @hide
@@ -8224,6 +8246,24 @@
public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model";
/**
+ * How often to check for location access.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
+ "location_access_check_interval_millis";
+
+ /**
+ * Delay between granting location access and checking it.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
+ "location_access_check_delay_millis";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -9339,6 +9379,13 @@
"location_background_throttle_package_whitelist";
/**
+ * Whether to disable location status callbacks in preparation for deprecation.
+ * @hide
+ */
+ public static final String LOCATION_DISABLE_STATUS_CALLBACKS =
+ "location_disable_status_callbacks";
+
+ /**
* Maximum staleness allowed for last location when returned to clients with only foreground
* location permissions.
* @hide
@@ -10773,6 +10820,41 @@
public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
/**
+ * The threshold value for the number of consecutive dns timeout events received to be a
+ * signal of data stall. Set the value to 0 or less than 0 to disable. Note that the value
+ * should be larger than 0 if the DNS data stall detection is enabled.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
+ "data_stall_consecutive_dns_timeout_threshold";
+
+ /**
+ * The minimal time interval in milliseconds for data stall reevaluation.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_MIN_EVALUATE_INTERVAL =
+ "data_stall_min_evaluate_interval";
+
+ /**
+ * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting
+ * a data stall.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD =
+ "data_stall_valid_dns_time_threshold";
+
+ /**
+ * Which data stall detection signal to use. Possible values are a union of the powers of 2
+ * of DATA_STALL_EVALUATION_TYPE_*.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
+
+ /**
* Whether network service discovery is enabled.
*
* @hide
@@ -10993,6 +11075,16 @@
= "activity_starts_logging_enabled";
/**
+ * Feature flag to enable or disable the background activity starts.
+ * When disabled, apps aren't allowed to start activities unless they're in the foreground.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ */
+ public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED =
+ "background_activity_starts_enabled";
+
+ /**
* @hide
* @see com.android.server.appbinding.AppBindingConstants
*/
@@ -11558,7 +11650,7 @@
/**
* Whether or not show hidden launcher icon apps feature is enabled.
* Type: int (0 for false, 1 for true)
- * Default: 0
+ * Default: 1
* @hide
*/
public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
@@ -12138,6 +12230,20 @@
"smart_selection_metadata_url";
/**
+ * URL for conversation actions model updates
+ * @hide
+ */
+ public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL =
+ "conversation_actions_content_url";
+
+ /**
+ * URL for conversation actions model update metadata
+ * @hide
+ */
+ public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL =
+ "conversation_actions_metadata_url";
+
+ /**
* SELinux enforcement status. If 0, permissive; if 1, enforcing.
* @hide
*/
@@ -12687,6 +12793,17 @@
public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
/**
+ * Used to emulate Smart Suggestion for Augmented Autofill during development
+ *
+ * <p>Valid values: {@code 0x1} for IME and/or {@code 0x2} for popup window.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS =
+ "autofill_smart_suggestion_emulation_flags";
+
+ /**
* Exemptions to the hidden API blacklist.
*
* @hide
@@ -12733,6 +12850,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.
*
@@ -13405,6 +13539,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
@@ -13541,6 +13682,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
@@ -13686,6 +13843,15 @@
*/
public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
+
+ /**
+ * Whether we've enabled native flags health check on this device. Takes effect on
+ * reboot. The value "1" enables native flags health check; otherwise it's disabled.
+ * @hide
+ */
+ public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
+ "native_flags_health_check_enabled";
+
}
/**
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/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
new file mode 100644
index 0000000..68a86f3
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.AugmentedAutofillService.AutofillProxy;
+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
+ if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ }
+ // 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;
+ public final AutofillId focusedId;
+ public final AutofillValue focusedValue;
+
+ // 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.focusedId = focusedId;
+ this.focusedValue = 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(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(mSessionId, ids, values);
+ }
+
+ public void setFillWindow(@NonNull FillWindow fillWindow) {
+ synchronized (mLock) {
+ mFillWindow = fillWindow;
+ }
+ }
+
+ public FillWindow getFillWindow() {
+ synchronized (mLock) {
+ return mFillWindow;
+ }
+ }
+
+ // 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(focusedId);
+ if (focusedValue != null) {
+ pw.print(prefix); pw.print("focusedValue: "); pw.println(focusedValue);
+ }
+ 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/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
new file mode 100644
index 0000000..0546465
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+
+/**
+ * Callback used to indicate at {@link FillRequest} has been fulfilled.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillCallback {
+ private final AutofillProxy mProxy;
+
+ FillCallback(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * Sets the response associated with the request.
+ *
+ * @param response response associated with the request, or {@code null} if the service
+ * could not provide autofill for the request.
+ */
+ public void onSuccess(@Nullable FillResponse response) {
+ mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS);
+ final FillWindow fillWindow = response.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.show();
+ }
+ // TODO(b/111330312): properly implement on server-side by updating the Session state
+ // accordingly (and adding CTS tests)
+ }
+}
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
new file mode 100644
index 0000000..e65cf47
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill.augmented;
+
+import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Object used to interact with the autofill system.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillController {
+ private static final String TAG = "FillController";
+
+ private final AutofillProxy mProxy;
+
+ FillController(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * Fills the activity with the provided values.
+ *
+ * <p>As a side effect, the {@link FillWindow} associated with the {@link FillResponse} will be
+ * automatically {@link FillWindow#destroy() destroyed}.
+ */
+ public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
+ Preconditions.checkNotNull(values);
+
+ if (DEBUG) {
+ Log.d(TAG, "autofill() with " + values.size() + " values");
+ }
+
+ try {
+ mProxy.autofill(values);
+ final FillWindow fillWindow = mProxy.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.destroy();
+ }
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
new file mode 100644
index 0000000..fd75b15
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/FillRequest.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 android.service.autofill.augmented;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+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;
+
+ /** @hide */
+ FillRequest(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * 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 ComponentName getActivityComponent() {
+ return mProxy.componentName;
+ }
+
+ /**
+ * Gets the id of the field that triggered the request.
+ */
+ @NonNull
+ public AutofillId getFocusedId() {
+ return mProxy.focusedId;
+ }
+
+ /**
+ * Gets the current value of the field that triggered the request.
+ */
+ @NonNull
+ public AutofillValue getFocusedValue() {
+ return mProxy.focusedValue;
+ }
+
+ /**
+ * Gets the Smart Suggestions object used to embed the autofill UI.
+ *
+ * @return object used to embed the autofill UI, or {@code null} if not supported.
+ */
+ @Nullable
+ public PresentationParams getPresentationParams() {
+ return mProxy.getSmartSuggestionParams();
+ }
+
+ @Override
+ public String toString() {
+ return "FillRequest[act=" + getActivityComponent().flattenToShortString()
+ + ", id=" + mProxy.focusedId + "]";
+ }
+}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
new file mode 100644
index 0000000..7064b6f
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/FillResponse.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.service.autofill.augmented;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.util.List;
+
+/**
+ * Response to a {@link FillRequest}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillResponse implements Parcelable {
+
+ private final FillWindow mFillWindow;
+
+ private FillResponse(@NonNull Builder builder) {
+ mFillWindow = builder.mFillWindow;
+ }
+
+ /** @hide */
+ @Nullable
+ FillWindow getFillWindow() {
+ return mFillWindow;
+ }
+
+ /**
+ * Builder for {@link FillResponse} objects.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+
+ private FillWindow mFillWindow;
+
+ /**
+ * Sets the {@link FillWindow} used to display the Autofill UI.
+ *
+ * <p>Must be called when the service is handling the request so the Android System can
+ * properly synchronize the UI.
+ *
+ * @return this builder
+ */
+ public Builder setFillWindow(@NonNull FillWindow fillWindow) {
+ // TODO(b/111330312): implement / check not null / unit test
+ // TODO(b/111330312): throw exception if FillWindow not updated yet
+ mFillWindow = fillWindow;
+ return this;
+ }
+
+ /**
+ * Tells the Android System that the given {@code ids} should not trigger further
+ * {@link FillRequest requests} when focused.
+ *
+ * @param ids ids of the fields that should be ignored
+ *
+ * @return this builder
+ */
+ public Builder setIgnoredIds(@NonNull List<AutofillId> ids) {
+ // TODO(b/111330312): implement / check not null / unit test
+ return this;
+ }
+
+ /**
+ * Builds a new {@link FillResponse} instance.
+ *
+ * @throws IllegalStateException if any of the following conditions occur:
+ * <ol>
+ * <li>{@link #build()} was already called.
+ * <li>No call was made to {@link #setFillWindow(FillWindow)} or
+ * {@ling #setIgnoredIds(List<AutofillId>)}.
+ * </ol>
+ *
+ * @return A built response.
+ */
+ public FillResponse build() {
+ // TODO(b/111330312): check conditions / add unit test
+ return new FillResponse(this);
+ }
+
+ // TODO(b/111330312): add methods to disable app / activity, either here or on manager
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ // TODO(b/111330312): implement
+ }
+
+ public static final Parcelable.Creator<FillResponse> CREATOR =
+ new Parcelable.Creator<FillResponse>() {
+
+ @Override
+ public FillResponse createFromParcel(Parcel parcel) {
+ // TODO(b/111330312): implement
+ return null;
+ }
+
+ @Override
+ public FillResponse[] newArray(int size) {
+ return new FillResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
new file mode 100644
index 0000000..9e3aba4
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 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.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.service.autofill.augmented.PresentationParams.Area;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+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;
+
+/**
+ * Handle to a window used to display the augmented autofill UI.
+ *
+ * <p>The steps to create an augmented autofill UI are:
+ *
+ * <ol>
+ * <li>Gets the {@link PresentationParams} from the {@link FillRequest}.
+ * <li>Gets the {@link Area} to display the UI (for example, through
+ * {@link PresentationParams#getSuggestionArea()}.
+ * <li>Creates a {@link View} that must fit in the {@link Area#getBounds() area boundaries}.
+ * <li>Set the proper listeners to the view (for example, a click listener that
+ * triggers {@link FillController#autofill(java.util.List)}
+ * <li>Call {@link #update(Area, View, long)} with these arguments.
+ * <li>Create a {@link FillResponse} with the {@link FillWindow}.
+ * <li>Pass such {@link FillResponse} to {@link FillCallback#onSuccess(FillResponse)}.
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillWindow implements AutoCloseable {
+ private static final String TAG = "FillWindow";
+
+ /** Indicates the data being shown is a physical address */
+ public static final long FLAG_METADATA_ADDRESS = 0x1;
+
+ // TODO(b/111330312): add moar flags
+
+ /** @hide */
+ @LongDef(prefix = { "FLAG" }, value = {
+ FLAG_METADATA_ADDRESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flags{}
+
+ private final Object mLock = new Object();
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ @GuardedBy("mLock")
+ private Dialog mDialog;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed;
+
+ private AutofillProxy mProxy;
+
+ /**
+ * Updates the content of the window.
+ *
+ * @param rootView new root view
+ * @param area coordinates to render the view.
+ * @param flags optional flags such as metadata of what will be rendered in the window. The
+ * Smart Suggestion host might decide whether or not to render the UI based on them.
+ *
+ * @return boolean whether the window was updated or not.
+ *
+ * @throws IllegalArgumentException if the area is not compatible with this window
+ */
+ public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) {
+ if (DEBUG) {
+ Log.d(TAG, "Updating " + area + " + with " + rootView);
+ }
+ // TODO(b/111330312): add test case for null
+ Preconditions.checkNotNull(area);
+ Preconditions.checkNotNull(rootView);
+ // TODO(b/111330312): must check the area is a valid object returned by
+ // SmartSuggestionParams, throw IAE if not
+
+ // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider
+
+
+ // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating
+ // the window underneath the existing view.
+
+ final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams();
+ if (smartSuggestion == null) {
+ Log.w(TAG, "No SmartSuggestionParams");
+ return false;
+ }
+
+ final Rect rect = area.getBounds();
+ if (rect == null) {
+ Log.wtf(TAG, "No Rect on SmartSuggestionParams");
+ return false;
+ }
+
+ 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
+ // rect boundaries.
+ destroy();
+
+ // TODO(b/111330312): make sure all touch events are handled, window is always closed,
+ // etc.
+
+ mDialog = new Dialog(rootView.getContext());
+ mCloseGuard.open("destroy");
+ final Window window = mDialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+
+ final int height = rect.bottom - rect.top;
+ final int width = rect.right - rect.left;
+ final WindowManager.LayoutParams windowParams = window.getAttributes();
+ windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ windowParams.y = rect.top - height;
+ windowParams.height = height;
+ windowParams.x = rect.left;
+ windowParams.width = width;
+
+ window.setAttributes(windowParams);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
+ mDialog.setContentView(rootView, diagParams);
+
+ if (DEBUG) {
+ Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
+ }
+
+ mProxy.setFillWindow(this);
+ return true;
+ }
+ }
+
+ /** @hide */
+ void show() {
+ // TODO(b/111330312): check if updated first / throw exception
+ if (DEBUG) Log.d(TAG, "show()");
+
+ synchronized (mLock) {
+ checkNotDestroyedLocked();
+ if (mDialog == null) {
+ throw new IllegalStateException("update() not called yet, or already destroyed()");
+ }
+
+ mDialog.show();
+ if (mProxy != null) {
+ mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
+ }
+ }
+ }
+
+ /**
+ * Destroys the window.
+ *
+ * <p>Once destroyed, this window cannot be used anymore
+ */
+ public void destroy() {
+ if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
+
+ synchronized (this) {
+ if (mDestroyed || mDialog == null) return;
+
+ 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();
+ }
+ }
+
+ private void checkNotDestroyedLocked() {
+ if (mDestroyed) {
+ throw new IllegalStateException("already destroyed()");
+ }
+ }
+
+ /** @hide */
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ synchronized (this) {
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ if (mDialog != null) {
+ pw.print(prefix); pw.print("dialog: ");
+ pw.println(mDialog.isShowing() ? "shown" : "hidden");
+ pw.print(prefix); pw.print("window: ");
+ pw.println(mDialog.getWindow().getAttributes());
+ }
+ }
+ }
+
+ /** @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/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
new file mode 100644
index 0000000..0124ecc
--- /dev/null
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.util.DebugUtils;
+import android.view.View;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
+ * the intelligence service.
+ *
+ * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
+ *
+ * <ul>
+ * <li>A small area associated with suggestions (like a small strip in the top of the IME),
+ * returned by {@link #getSuggestionArea()}
+ * <li>The full area (like the full IME window), returned by {@link #getFullArea()}
+ * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
+ * </ul>
+ *
+ * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
+ * dimensions the smart suggestion window, so the service can use it to calculate the size of the
+ * view that will be passed to {@link FillWindow#update(Area, View, long)}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class PresentationParams {
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the top of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the left of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the right of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted by the IME.
+ */
+ public static final int FLAG_HOST_IME = 0x10;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
+ * window.
+ */
+ public static final int FLAG_HOST_SYSTEM = 0x20;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_HINT_GRAVITY_TOP,
+ FLAG_HINT_GRAVITY_BOTTOM,
+ FLAG_HINT_GRAVITY_LEFT,
+ FLAG_HINT_GRAVITY_RIGHT,
+ FLAG_HOST_IME,
+ FLAG_HOST_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flags {}
+
+
+ // /** @hide */
+ PresentationParams() {}
+
+ /**
+ * Gets the area of the suggestion strip for the given {@code metadata}
+ *
+ * @return strip dimensions, or {@code null} if the Smart Suggestion provider does not support
+ * suggestions strip.
+ */
+ @Nullable
+ public Area getSuggestionArea() {
+ return null;
+ }
+
+ /**
+ * Gets the full area for the of the Smart Suggestion provider.
+ *
+ * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
+ * embeding the UI on its full area.
+ */
+ @Nullable
+ public Area getFullArea() {
+ return null;
+ }
+
+ /**
+ * Gets flags associated with the Smart Suggestion.
+ *
+ * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
+ * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
+ * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
+ * {@link #FLAG_HOST_SYSTEM},
+ */
+ public @Flags int getFlags() {
+ return 0;
+ }
+
+ /** @hide */
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ final int flags = getFlags();
+ if (flags > 0) {
+ pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
+ }
+ }
+
+ private static String flagsToString(int flags) {
+ return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
+ }
+
+ /**
+ * Area associated with a {@link PresentationParams Smart Suggestions} provider.
+ *
+ * @hide
+ * */
+ @SystemApi
+ public abstract static class Area {
+
+ /** @hide */
+ public final AutofillProxy proxy;
+
+ private final Rect mBounds;
+
+ private Area(@NonNull AutofillProxy proxy, @NonNull Rect bounds) {
+ this.proxy = proxy;
+ mBounds = bounds;
+ }
+
+ /**
+ * Gets the area boundaries.
+ */
+ @NonNull
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ /**
+ * Gets a subarea limited by given boundaries.
+ *
+ * @param bounds boundaries relative to this 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) {
+ // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return mBounds.toString();
+ }
+ }
+
+ /**
+ * System-provided poup window anchored to a view.
+ *
+ * <p>Used just for debugging purposes.
+ *
+ * @hide
+ */
+ public static final class SystemPopupPresentationParams extends PresentationParams {
+ private final Area mSuggestionArea;
+
+ public SystemPopupPresentationParams(@NonNull AutofillProxy proxy, @NonNull Rect rect) {
+ mSuggestionArea = new Area(proxy, rect) {};
+ }
+
+ @Override
+ public Area getSuggestionArea() {
+ return mSuggestionArea;
+ }
+
+ @Override
+ public int getFlags() {
+ return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
+ }
+
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+ pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
+ }
+ }
+}
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl
similarity index 87%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl
index 31d1339..c032cfd 100644
--- a/core/java/android/service/intelligence/SnapshotData.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 SnapshotData;
+parcelable ContentCaptureEventsRequest;
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
new file mode 100644
index 0000000..df58f52
--- /dev/null
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.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 android.service.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.contentcapture.ContentCaptureEvent;
+
+import java.util.List;
+
+/**
+ * Batch of content capture events.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ContentCaptureEventsRequest implements Parcelable {
+
+ private final List<ContentCaptureEvent> mEvents;
+
+ /** @hide */
+ public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
+ mEvents = events;
+ }
+
+ /**
+ * Gets the events.
+ */
+ @NonNull
+ public List<ContentCaptureEvent> getEvents() {
+ return mEvents;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedList(mEvents, flags);
+ }
+
+ public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
+ new Parcelable.Creator<ContentCaptureEventsRequest>() {
+
+ @Override
+ public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
+ return new ContentCaptureEventsRequest(parcel
+ .createTypedArrayList(ContentCaptureEvent.CREATOR));
+ }
+
+ @Override
+ public ContentCaptureEventsRequest[] newArray(int size) {
+ return new ContentCaptureEventsRequest[size];
+ }
+ };
+}
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 0cc377b..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.IntelligenceManager#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 709c3b7..0000000
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ /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 android.service.intelligence;
-
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.InteractionContext;
-import android.service.intelligence.SnapshotData;
-
-import android.view.intelligence.ContentCaptureEvent;
-
-import java.util.List;
-
-
-/**
- * Interface from the system to an intelligence service.
- *
- * @hide
- */
-oneway interface IIntelligenceService {
-
- // Called when session is created (context not null) or destroyed (context null)
- void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
-
- void onContentCaptureEvents(in InteractionSessionId sessionId,
- in List<ContentCaptureEvent> events);
-
- void onActivitySnapshot(in InteractionSessionId sessionId,
- in SnapshotData snapshotData);
-}
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
deleted file mode 100644
index 27569b6..0000000
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ /dev/null
@@ -1,145 +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.SystemApi;
-import android.app.Service;
-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.intelligence.ContentCaptureEvent;
-
-import java.util.List;
-
-/**
- * A service used to capture the content 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.
- *
- * @hide
- */
-@SystemApi
-public abstract class IntelligenceService extends Service {
-
- private static final String TAG = "IntelligenceService";
-
- /**
- * 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_INTELLIGENCE_SERVICE} permission so
- * that other applications can not abuse it.
- */
- public static final String SERVICE_INTERFACE =
- "android.service.intelligence.IntelligenceService";
-
- private Handler mHandler;
-
- private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
-
- @Override
- public void onSessionLifecycle(InteractionContext context, InteractionSessionId sessionId)
- throws RemoteException {
- if (context != null) {
- mHandler.sendMessage(
- obtainMessage(IntelligenceService::onCreateInteractionSession,
- IntelligenceService.this, context, sessionId));
- } else {
- mHandler.sendMessage(
- obtainMessage(IntelligenceService::onDestroyInteractionSession,
- IntelligenceService.this, sessionId));
- }
- }
-
- @Override
- public void onContentCaptureEvents(InteractionSessionId sessionId,
- List<ContentCaptureEvent> events) {
- mHandler.sendMessage(
- obtainMessage(IntelligenceService::onContentCaptureEvent,
- IntelligenceService.this, sessionId, events));
-
- }
-
- @Override
- public void onActivitySnapshot(InteractionSessionId sessionId,
- SnapshotData snapshotData) {
- mHandler.sendMessage(
- obtainMessage(IntelligenceService::onActivitySnapshot,
- IntelligenceService.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: " + intent);
- 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) {}
-
- /**
- * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
- * session.
- *
- * @param sessionId the session's Id
- * @param events the events
- */
- // TODO(b/111276913): rename to onContentCaptureEvents
- public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
- @NonNull List<ContentCaptureEvent> events);
-
- /**
- * Notifies the service of {@link IntelligenceSnapshotData 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) {}
-}
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/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d8bd002..1ddc099e 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -16,6 +16,7 @@
package android.service.notification;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.pm.ParceledListSlice;
@@ -47,4 +48,8 @@
void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
+ void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+ void onNotificationDirectReply(String key);
+ void onSuggestedReplySent(String key, in CharSequence reply, int source);
+ void onActionClicked(String key, in Notification.Action action, int source);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c1a3c2b..c850a4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,9 +16,14 @@
package android.service.notification;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -33,6 +38,7 @@
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
import java.util.List;
/**
@@ -63,6 +69,13 @@
public abstract class NotificationAssistantService extends NotificationListenerService {
private static final String TAG = "NotificationAssistants";
+ /** @hide */
+ @Retention(SOURCE)
+ @IntDef({SOURCE_FROM_APP, SOURCE_FROM_ASSISTANT})
+ public @interface Source {}
+ public static final int SOURCE_FROM_APP = 0;
+ public static final int SOURCE_FROM_ASSISTANT = 1;
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -160,6 +173,38 @@
}
/**
+ * Implement this to know when a notification is expanded / collapsed.
+ * @param key the notification key
+ * @param isUserAction whether the expanded change is caused by user action.
+ * @param isExpanded whether the notification is expanded.
+ */
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {}
+
+ /**
+ * Implement this to know when a direct reply is sent from a notification.
+ * @param key the notification key
+ */
+ public void onNotificationDirectReply(String key) {}
+
+ /**
+ * Implement this to know when a suggested reply is sent.
+ * @param key the notification key
+ * @param reply the reply that is just sent
+ * @param source the source that provided the reply, e.g. SOURCE_FROM_APP
+ */
+ public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
+
+ /**
+ * Implement this to know when an action is clicked.
+ * @param key the notification key
+ * @param action the action that is just clicked
+ * @param source the source that provided the action, e.g. SOURCE_FROM_APP
+ */
+ public void onActionClicked(String key, @Nullable Notification.Action action, int source) {
+ }
+
+ /**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
@@ -255,12 +300,53 @@
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATIONS_SEEN,
args).sendToTarget();
}
+
+ @Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.argi1 = isUserAction ? 1 : 0;
+ args.argi2 = isExpanded ? 1 : 0;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_EXPANSION_CHANGED, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = reply;
+ args.argi2 = source;
+ mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget();
+ }
+
+ @Override
+ public void onActionClicked(String key, Notification.Action action, int source) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = action;
+ args.argi2 = source;
+ mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget();
+ }
}
private final class MyHandler extends Handler {
public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
public static final int MSG_ON_NOTIFICATIONS_SEEN = 3;
+ public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
+ public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
+ public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
+ public static final int MSG_ON_ACTION_CLICKED = 7;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -305,6 +391,40 @@
onNotificationsSeen(keys);
break;
}
+ case MSG_ON_NOTIFICATION_EXPANSION_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ boolean isUserAction = args.argi1 == 1;
+ boolean isExpanded = args.argi2 == 1;
+ args.recycle();
+ onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ break;
+ }
+ case MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ args.recycle();
+ onNotificationDirectReply(key);
+ break;
+ }
+ case MSG_ON_SUGGESTED_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ CharSequence reply = (CharSequence) args.arg2;
+ int source = args.argi2;
+ args.recycle();
+ onSuggestedReplySent(key, reply, source);
+ break;
+ }
+ case MSG_ON_ACTION_CLICKED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ Notification.Action action = (Notification.Action) args.arg2;
+ int source = args.argi2;
+ args.recycle();
+ onActionClicked(key, action, source);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64eae0c..1fe97b7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,27 @@
}
@Override
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onActionClicked(String key, Notification.Action action, int source) {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationChannelModification(String pkgName, UserHandle user,
NotificationChannel channel,
@ChannelOrGroupModificationTypes int modificationType) {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 8371c31b..0e2ae83 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -806,7 +806,7 @@
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
- messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
+ callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 43ab8dc..194147c 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -859,6 +859,27 @@
/**
* @hide
*/
+ public boolean areValuesSet() {
+ return getPriorityCategoryReminders() != STATE_UNSET
+ || getPriorityCategoryEvents() != STATE_UNSET
+ || getPriorityCategoryMessages() != STATE_UNSET
+ || getPriorityCategoryCalls() != STATE_UNSET
+ || getPriorityCategoryRepeatCallers() != STATE_UNSET
+ || getPriorityCategoryAlarms() != STATE_UNSET
+ || getPriorityCategoryMedia() != STATE_UNSET
+ || getPriorityCategorySystem() != STATE_UNSET
+ || getVisualEffectFullScreenIntent() != STATE_UNSET
+ || getVisualEffectLights() != STATE_UNSET
+ || getVisualEffectPeek() != STATE_UNSET
+ || getVisualEffectStatusBar() != STATE_UNSET
+ || getVisualEffectBadge() != STATE_UNSET
+ || getVisualEffectAmbient() != STATE_UNSET
+ || getVisualEffectNotificationList() != STATE_UNSET;
+ }
+
+ /**
+ * @hide
+ */
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl
index d5e10d6..99cffc5 100644
--- a/core/java/android/service/oemlock/IOemLockService.aidl
+++ b/core/java/android/service/oemlock/IOemLockService.aidl
@@ -22,6 +22,8 @@
* @hide
*/
interface IOemLockService {
+ String getLockName();
+
void setOemUnlockAllowedByCarrier(boolean allowed, in byte[] signature);
boolean isOemUnlockAllowedByCarrier();
diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java
index f0d6603..029d645 100644
--- a/core/java/android/service/oemlock/OemLockManager.java
+++ b/core/java/android/service/oemlock/OemLockManager.java
@@ -44,6 +44,23 @@
}
/**
+ * Returns a vendor specific name for the OEM lock.
+ *
+ * This value is used to identify the security protocol used by locks.
+ *
+ * @return The name of the OEM lock or {@code null} if failed to get the name.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE)
+ @Nullable
+ public String getLockName() {
+ try {
+ return mService.getLockName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets whether the carrier has allowed this device to be OEM unlocked.
*
* Depending on the implementation, the validity of the request might need to be proved. This
diff --git a/core/java/android/service/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
new file mode 100644
index 0000000..5fb7249
--- /dev/null
+++ b/core/java/android/service/sms/FinancialSmsService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.sms;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.database.CursorWindow;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * A service to support sms messages read for financial apps.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class FinancialSmsService extends Service {
+
+ private static final String TAG = "FinancialSmsService";
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest for the system to recognize it as a quota providing
+ * service.
+ */
+ public static final String ACTION_FINANCIAL_SERVICE_INTENT =
+ "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+
+ /** {@hide} **/
+ public static final String EXTRA_SMS_MSGS = "sms_messages";
+
+ private FinancialSmsServiceWrapper mWrapper;
+
+ private void getSmsMessages(RemoteCallback callback, Bundle params) {
+ final Bundle data = new Bundle();
+ CursorWindow smsMessages = onGetSmsMessages(params);
+ if (smsMessages != null) {
+ data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
+ }
+ callback.sendResult(data);
+ }
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+ /** @hide */
+ public FinancialSmsService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new FinancialSmsServiceWrapper();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Get sms messages for financial apps.
+ *
+ * @param params parameters passed in by the calling app.
+ * @return the {@code CursorWindow} with all sms messages for the app to read.
+ *
+ * {@hide}
+ */
+ @Nullable
+ @SystemApi
+ public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
+
+ private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
+ @Override
+ public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
+ mHandler.sendMessage(obtainMessage(
+ FinancialSmsService::getSmsMessages,
+ FinancialSmsService.this,
+ callback, params));
+ }
+ }
+
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/android/service/sms/IFinancialSmsService.aidl
similarity index 70%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/android/service/sms/IFinancialSmsService.aidl
index 27d25b8..caabe58 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/android/service/sms/IFinancialSmsService.aidl
@@ -13,12 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package android.service.sms;
+
+import android.os.Bundle;
+import android.os.RemoteCallback;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * Service used by financial apps to read sms messages.
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
-}
+oneway interface IFinancialSmsService
+{
+ void getSmsMessages(in RemoteCallback callback, in Bundle params);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index dccce40..ebce484 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -27,7 +27,7 @@
void setDesiredSize(int width, int height);
void setDisplayPadding(in Rect padding);
void setVisibility(boolean visible);
- void setInAmbientMode(boolean inAmbientDisplay, boolean animated);
+ void setInAmbientMode(boolean inAmbientDisplay, long animationDuration);
void dispatchPointer(in MotionEvent event);
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6f51bec..a095b0d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.app.WallpaperColors;
@@ -56,6 +57,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
+import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -129,7 +131,7 @@
Bundle extras;
boolean sync;
}
-
+
/**
* The actual implementation of a wallpaper. A wallpaper service may
* have multiple instances running (for example as a real wallpaper
@@ -144,7 +146,7 @@
HandlerCaller mCaller;
IWallpaperConnection mConnection;
IBinder mWindowToken;
-
+
boolean mInitializing = true;
boolean mVisible;
boolean mReportedVisible;
@@ -184,6 +186,7 @@
final DisplayCutout.ParcelableWrapper mDisplayCutout =
new DisplayCutout.ParcelableWrapper();
DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
+ final InsetsState mInsetsState = new InsetsState();
final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
final WindowManager.LayoutParams mLayout
@@ -208,7 +211,6 @@
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
- DisplayManager mDisplayManager;
Display mDisplay;
private int mDisplayState;
@@ -419,7 +421,7 @@
public int getDesiredMinimumHeight() {
return mIWallpaperEngine.mReqHeight;
}
-
+
/**
* Return whether the wallpaper is currently visible to the user,
* this is the last value supplied to
@@ -441,7 +443,9 @@
/**
* Returns true if this engine is running in ambient mode -- that is,
* it is being shown in low power mode, on always on display.
+ * @hide
*/
+ @SystemApi
public boolean isInAmbientMode() {
return mIsInAmbientMode;
}
@@ -567,14 +571,16 @@
* Called when the device enters or exits ambient mode.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if you'll have the opportunity of animating your transition
- * {@code false} when the wallpaper should present its ambient version
- * immediately.
+ * @param animationDuration How long the transition animation to change the ambient state
+ * should run, in milliseconds. If 0 is passed as the argument
+ * here, the state should be switched immediately.
*
* @see #isInAmbientMode()
* @see WallpaperInfo#supportsAmbientMode()
+ * @hide
*/
- public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ @SystemApi
+ public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
}
/**
@@ -804,9 +810,11 @@
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
mInputChannel = new InputChannel();
+
if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
- mOutsets, mDisplayCutout, mInputChannel) < 0) {
+ mOutsets, mDisplayCutout, mInputChannel,
+ mInsetsState) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -832,7 +840,8 @@
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
- mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
+ mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+ mInsetsState);
if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ ", frame=" + mWinFrame);
@@ -1015,14 +1024,6 @@
return;
}
- mDisplayManager = getSystemService(DisplayManager.class);
- mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
- if (mDisplay == null) {
- // TODO(b/115486823) Ignore this engine.
- Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
- return;
- }
-
mIWallpaperEngine = wrapper;
mCaller = wrapper.mCaller;
mConnection = wrapper.mConnection;
@@ -1034,13 +1035,16 @@
mWindow.setSession(mSession);
mLayout.packageName = getPackageName();
- mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
+ mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
+ mCaller.getHandler());
+ mDisplay = mIWallpaperEngine.mDisplay;
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
onCreate(mSurfaceHolder);
-
+
mInitializing = false;
+
mReportedVisible = false;
updateSurface(false, false, false);
}
@@ -1050,19 +1054,19 @@
* message sent from handler.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if the transition will be animated.
+ * @param animationDuration For how long the transition will last, in ms.
* @hide
*/
@VisibleForTesting
- public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (!mDestroyed) {
if (DEBUG) {
Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", "
- + animated + "): " + this);
+ + animationDuration + "): " + this);
}
mIsInAmbientMode = inAmbientMode;
if (mCreated) {
- onAmbientModeChanged(inAmbientMode, animated);
+ onAmbientModeChanged(inAmbientMode, animationDuration);
}
}
}
@@ -1202,8 +1206,8 @@
mDestroyed = true;
- if (mDisplayManager != null) {
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ if (mIWallpaperEngine.mDisplayManager != null) {
+ mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
}
if (mVisible) {
@@ -1272,7 +1276,9 @@
int mReqWidth;
int mReqHeight;
final Rect mDisplayPadding = new Rect();
- int mDisplayId;
+ final int mDisplayId;
+ final DisplayManager mDisplayManager;
+ final Display mDisplay;
Engine mEngine;
@@ -1289,7 +1295,15 @@
mReqHeight = reqHeight;
mDisplayPadding.set(padding);
mDisplayId = displayId;
-
+
+ // Create a display context before onCreateEngine.
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplay = mDisplayManager.getDisplay(mDisplayId);
+
+ if (mDisplay == null) {
+ // Ignore this engine.
+ throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
+ }
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
}
@@ -1311,10 +1325,10 @@
}
@Override
- public void setInAmbientMode(boolean inAmbientDisplay, boolean animated)
+ public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration)
throws RemoteException {
- Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
- animated ? 1 : 0);
+ Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
+ animationDuration);
mCaller.sendMessage(msg);
}
@@ -1385,7 +1399,7 @@
return;
}
case DO_IN_AMBIENT_MODE: {
- mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0);
+ mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj);
return;
}
case MSG_UPDATE_SURFACE:
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ec63cd9..7815864 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1009,7 +1009,7 @@
Log.e(TAG, "null synthesis text");
return false;
}
- if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) {
+ if (mText.length() > TextToSpeech.getMaxSpeechInputLength()) {
Log.w(TAG, "Text too long: " + mText.length() + " chars");
return false;
}
@@ -1609,7 +1609,7 @@
synchronized (mCallerToCallback) {
mCallerToCallback.remove(caller);
}
- //mSynthHandler.stopForApp(caller);
+ mSynthHandler.stopForApp(caller);
}
@Override
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 2cf0262..8cb18b2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -485,7 +485,7 @@
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521430)
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align, TextDirectionHeuristic textDir,
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index b970c25..85b6b88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -46,6 +46,7 @@
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.LineBackgroundSpan;
+import android.text.style.LineHeightSpan;
import android.text.style.LocaleSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
@@ -733,7 +734,9 @@
/** @hide */
public static final int LINE_BACKGROUND_SPAN = 27;
/** @hide */
- public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
+ public static final int LINE_HEIGHT_SPAN = 28;
+ /** @hide */
+ public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -928,6 +931,10 @@
readSpan(p, sp, new LineBackgroundSpan.Standard(p));
break;
+ case LINE_HEIGHT_SPAN:
+ readSpan(p, sp, new LineHeightSpan.Standard(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 562ae7a..bab4bc3 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -18,8 +18,8 @@
import android.util.TimeFormatException;
+import libcore.timezone.ZoneInfoDB;
import libcore.util.ZoneInfo;
-import libcore.util.ZoneInfoDB;
import java.io.IOException;
import java.util.Locale;
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 2742ae0..a5d5af2 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -20,7 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Px;
import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
import android.text.TextPaint;
+import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -71,7 +74,7 @@
* covers only part of the paragraph.
* </p>
*/
- class Standard implements LineHeightSpan {
+ class Standard implements LineHeightSpan, ParcelableSpan {
private final @Px int mHeight;
/**
@@ -82,6 +85,48 @@
mHeight = height;
}
+ /**
+ * Constructor called from {@link TextUtils} to restore the span from a parcel
+ */
+ public Standard(Parcel src) {
+ mHeight = src.readInt();
+ }
+
+ /**
+ * Returns the line height specified by this span.
+ */
+ @Px
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.LINE_HEIGHT_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mHeight);
+ }
+
@Override
public void chooseHeight(@NonNull CharSequence text, int start, int end,
int spanstartv, int lineHeight,
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/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f4dad62..eef7ea2 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -29,6 +29,7 @@
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.URLSpan;
+import android.util.Log;
import android.util.Patterns;
import android.webkit.WebView;
import android.widget.TextView;
@@ -72,6 +73,9 @@
*/
public class Linkify {
+
+ private static final String LOG_TAG = "Linkify";
+
/**
* Bit field indicating that web URLs should be matched in methods that
* take an options mask
@@ -310,6 +314,11 @@
*/
private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
@Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+ if (text != null && containsUnsupportedCharacters(text.toString())) {
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return false;
+ }
+
if (mask == 0) {
return false;
}
@@ -356,6 +365,29 @@
}
/**
+ * Returns true if the specified text contains at least one unsupported character for applying
+ * links. Also logs the error.
+ *
+ * @param text the text to apply links to
+ * @hide
+ */
+ public static boolean containsUnsupportedCharacters(String text) {
+ if (text.contains("\u202C")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202C");
+ return true;
+ }
+ if (text.contains("\u202D")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202D");
+ return true;
+ }
+ if (text.contains("\u202E")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202E");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Scans the text of the provided TextView and turns all occurrences of
* the link types indicated in the mask into clickable links. If matches
* are found the movement method for the TextView is set to
@@ -560,6 +592,11 @@
@Nullable String defaultScheme, @Nullable String[] schemes,
@Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
@Nullable UrlSpanFactory urlSpanFactory) {
+ if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return false;
+ }
+
final String[] schemesCopy;
if (defaultScheme == null) defaultScheme = "";
if (schemes == null || schemes.length < 1) {
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index c822832..de182da 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.view.View;
@@ -109,7 +110,7 @@
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
new Property<View, PointF>(PointF.class, "bottomRight") {
@Override
@@ -144,7 +145,7 @@
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final Property<View, PointF> POSITION_PROPERTY =
new Property<View, PointF>(PointF.class, "position") {
@Override
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 7e499f2..b1fc17a 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,9 +39,9 @@
private int mLayoutId = -1;
private ViewGroup mSceneRoot;
private View mLayout; // alternative to layoutId
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Runnable mEnterAction;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Runnable mExitAction;
/**
@@ -200,7 +201,7 @@
*
* @param sceneRoot The view on which the current scene is being set
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5f348c4..dee6d90 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -45,7 +45,6 @@
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");
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 21c4252..57d55bf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,7 +24,7 @@
import libcore.timezone.CountryTimeZones;
import libcore.timezone.CountryTimeZones.TimeZoneMapping;
import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
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/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b8b7f3..5e6d3d1 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -24,6 +24,8 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.DisplayCutout;
+import android.view.InsetsState;
+import android.view.InsetsSourceControl;
import com.android.internal.os.IResultReceiver;
import android.util.MergedConfiguration;
@@ -53,6 +55,17 @@
in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
in DisplayCutout.ParcelableWrapper displayCutout);
+
+ /**
+ * Called when the window insets configuration has changed.
+ */
+ void insetsChanged(in InsetsState insetsState);
+
+ /**
+ * Called when this window retrieved control over a specified set of inset sources.
+ */
+ void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);
+
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 308a000..c4be0e5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -96,8 +96,9 @@
*/
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
- boolean scaleUp);
- void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
+ boolean scaleUp, int displayId);
+ void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter,
+ int displayId);
void executeAppTransition();
/**
@@ -294,14 +295,16 @@
void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled);
/**
- * Device has a software navigation bar (separate from the status bar).
+ * Device has a software navigation bar (separate from the status bar) on specific display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
*/
- boolean hasNavigationBar();
+ boolean hasNavigationBar(int displayId);
/**
* Get the position of the nav bar
*/
- int getNavBarPosition();
+ int getNavBarPosition(int displayId);
/**
* Lock the device immediately with the specified options (can be null).
@@ -548,4 +551,16 @@
* @see KeyguardManager#isDeviceLocked()
*/
void setShouldShowIme(int displayId, boolean shouldShow);
+
+ /**
+ * Reparent the top layers for a display to the requested surfaceControl. The display that
+ * is going to be re-parented (the displayId passed in) needs to have been created by the same
+ * process that is requesting the re-parent. This is to ensure clients can't just re-parent
+ * display content info to any SurfaceControl, as this would be a security issue.
+ *
+ * @param displayId The id of the display.
+ * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+ * display should be re-parented to.
+ */
+ void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index bedfa9f..9762586 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -28,6 +28,7 @@
import android.view.IWindowId;
import android.view.MotionEvent;
import android.view.WindowManager;
+import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -40,10 +41,11 @@
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
- out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
+ out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
+ out InsetsState insetsState);
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
- out Rect outStableInsets);
+ out Rect outStableInsets, out InsetsState insetsState);
void remove(IWindow window);
/**
@@ -86,6 +88,7 @@
* config for window, if it is now becoming visible and the merged configuration has changed
* since it was last displayed.
* @param outSurface Object in which is placed the new display surface.
+ * @param insetsState The current insets state in the system.
*
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
* {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -96,7 +99,8 @@
out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
out Rect outOutsets, out Rect outBackdropFrame,
out DisplayCutout.ParcelableWrapper displayCutout,
- out MergedConfiguration outMergedConfiguration, out Surface outSurface);
+ out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+ out InsetsState insetsState);
/*
* Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index dc1e505..5f6bc23 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -16,6 +16,8 @@
package android.view;
+import android.os.IBinder;
+
/**
* Functions as a handle for an application that can receive input.
* Enables the native input dispatcher to refer indirectly to the window manager's
@@ -28,19 +30,18 @@
@SuppressWarnings("unused")
private long ptr;
- // The window manager's application window token.
- public final Object appWindowToken;
-
// Application name.
public String name;
// Dispatching timeout.
public long dispatchingTimeoutNanos;
+ public IBinder token;
+
private native void nativeDispose();
- public InputApplicationHandle(Object appWindowToken) {
- this.appWindowToken = appWindowToken;
+ public InputApplicationHandle(IBinder token) {
+ this.token = token;
}
@Override
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 621ee89..92e0009 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -17,6 +17,7 @@
package android.view;
import android.graphics.Region;
+import android.os.IBinder;
import android.view.IWindow;
import android.view.InputChannel;
@@ -37,8 +38,8 @@
// The client window.
public final IWindow clientWindow;
- // The input channel associated with the window.
- public InputChannel inputChannel;
+ // The token assosciated with the window.
+ public IBinder token;
// The window name.
public String name;
@@ -56,6 +57,8 @@
public int frameRight;
public int frameBottom;
+ public int surfaceInset;
+
// Global scaling factor applied to touch events when they are dispatched
// to the window
public float scaleFactor;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
new file mode 100644
index 0000000..ba5340c
--- /dev/null
+++ b/core/java/android/view/InsetsController.java
@@ -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.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.InsetsState.InternalInsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link WindowInsetsController} on the client.
+ * @hide
+ */
+public class InsetsController implements WindowInsetsController {
+
+ private final InsetsState mState = new InsetsState();
+ private final Rect mFrame = new Rect();
+ private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+
+ private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+
+ void onFrameChanged(Rect frame) {
+ mFrame.set(frame);
+ }
+
+ public InsetsState getState() {
+ return mState;
+ }
+
+ public void setState(InsetsState state) {
+ mState.set(state);
+ }
+
+ /**
+ * @see InsetsState#calculateInsets
+ */
+ WindowInsets calculateInsets(boolean isScreenRound,
+ boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+ return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+ }
+
+ /**
+ * Called when the server has dispatched us a new set of inset controls.
+ */
+ public void onControlsChanged(InsetsSourceControl[] activeControls) {
+ if (activeControls != null) {
+ for (InsetsSourceControl activeControl : activeControls) {
+ mTmpControlArray.put(activeControl.getType(), activeControl);
+ }
+ }
+
+ // Ensure to update all existing source consumers
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
+
+ // control may be null, but we still need to update the control to null if it got
+ // revoked.
+ consumer.setControl(control);
+ }
+
+ // Ensure to create source consumers if not available yet.
+ for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
+ final InsetsSourceControl control = mTmpControlArray.valueAt(i);
+ getSourceConsumer(control.getType()).setControl(control);
+ }
+ mTmpControlArray.clear();
+ }
+
+ @Override
+ public void show(@InsetType int types) {
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ getSourceConsumer(internalTypes.valueAt(i)).show();
+ }
+ }
+
+ @Override
+ public void hide(@InsetType int types) {
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ getSourceConsumer(internalTypes.valueAt(i)).hide();
+ }
+ }
+
+ @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);
+ mSourceConsumers.put(type, controller);
+ return controller;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix); pw.println("InsetsController:");
+ mState.dump(prefix + " ", pw);
+ }
+}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
new file mode 100644
index 0000000..0cb8ad7
--- /dev/null
+++ b/core/java/android/view/InsetsSource.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 android.view;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the state of a single window generating insets for clients.
+ * @hide
+ */
+public class InsetsSource implements Parcelable {
+
+ private final @InternalInsetType int mType;
+
+ /** Frame of the source in screen coordinate space */
+ private final Rect mFrame;
+ private boolean mVisible;
+
+ private final Rect mTmpFrame = new Rect();
+
+ public InsetsSource(@InternalInsetType int type) {
+ mType = type;
+ mFrame = new Rect();
+ }
+
+ public InsetsSource(InsetsSource other) {
+ mType = other.mType;
+ mFrame = new Rect(other.mFrame);
+ mVisible = other.mVisible;
+ }
+
+ public void setFrame(Rect frame) {
+ mFrame.set(frame);
+ }
+
+ public void setVisible(boolean visible) {
+ mVisible = visible;
+ }
+
+ public @InternalInsetType int getType() {
+ return mType;
+ }
+
+ public Rect getFrame() {
+ return mFrame;
+ }
+
+ /**
+ * Calculates the insets this source will cause to a client window.
+ *
+ * @param relativeFrame The frame to calculate the insets relative to.
+ * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
+ * @return The resulting insets.
+ */
+ public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+ if (!ignoreVisibility && !mVisible) {
+ return Insets.NONE;
+ }
+ if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+ return Insets.NONE;
+ }
+
+ // Intersecting at top/bottom
+ if (mTmpFrame.width() == relativeFrame.width()) {
+ if (mTmpFrame.top == relativeFrame.top) {
+ return Insets.of(0, mTmpFrame.height(), 0, 0);
+ } else {
+ return Insets.of(0, 0, 0, mTmpFrame.height());
+ }
+ }
+ // Intersecting at left/right
+ else if (mTmpFrame.height() == relativeFrame.height()) {
+ if (mTmpFrame.left == relativeFrame.left) {
+ return Insets.of(mTmpFrame.width(), 0, 0, 0);
+ } else {
+ return Insets.of(0, 0, mTmpFrame.width(), 0);
+ }
+ } else {
+ return Insets.NONE;
+ }
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
+ pw.print(" frame="); pw.print(mFrame.toShortString());
+ pw.print(" visible="); pw.print(mVisible);
+ pw.println();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InsetsSource that = (InsetsSource) o;
+
+ if (mType != that.mType) return false;
+ if (mVisible != that.mVisible) return false;
+ return mFrame.equals(that.mFrame);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mType;
+ result = 31 * result + mFrame.hashCode();
+ result = 31 * result + (mVisible ? 1 : 0);
+ return result;
+ }
+
+ public InsetsSource(Parcel in) {
+ mType = in.readInt();
+ mFrame = in.readParcelable(null /* loader */);
+ mVisible = in.readBoolean();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeParcelable(mFrame, 0 /* flags*/);
+ dest.writeBoolean(mVisible);
+ }
+
+ public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
+
+ public InsetsSource createFromParcel(Parcel in) {
+ return new InsetsSource(in);
+ }
+
+ public InsetsSource[] newArray(int size) {
+ return new InsetsSource[size];
+ }
+ };
+}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
new file mode 100644
index 0000000..e74aa8d
--- /dev/null
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.Nullable;
+import android.view.SurfaceControl.Transaction;
+import android.view.InsetsState.InternalInsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Supplier;
+
+/**
+ * Controls the visibility and animations of a single window insets source.
+ * @hide
+ */
+public class InsetsSourceConsumer {
+
+ private final Supplier<Transaction> mTransactionSupplier;
+ private final @InternalInsetType int mType;
+ private final InsetsState mState;
+ private @Nullable InsetsSourceControl mControl;
+ private boolean mHidden;
+
+ public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
+ Supplier<Transaction> transactionSupplier) {
+ mType = type;
+ mState = state;
+ mTransactionSupplier = transactionSupplier;
+ }
+
+ public void setControl(@Nullable InsetsSourceControl control) {
+ if (mControl == control) {
+ return;
+ }
+ mControl = control;
+ applyHiddenToControl();
+ }
+
+ @VisibleForTesting
+ public InsetsSourceControl getControl() {
+ return mControl;
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ @VisibleForTesting
+ public void show() {
+ setHidden(false);
+ }
+
+ @VisibleForTesting
+ public void hide() {
+ setHidden(true);
+ }
+
+ private void setHidden(boolean hidden) {
+ if (mHidden == hidden) {
+ return;
+ }
+ mHidden = hidden;
+ applyHiddenToControl();
+ }
+
+ private void applyHiddenToControl() {
+ if (mControl == null) {
+ return;
+ }
+
+ // TODO: Animation
+ final Transaction t = mTransactionSupplier.get();
+ if (mHidden) {
+ t.hide(mControl.getLeash());
+ } else {
+ t.show(mControl.getLeash());
+ }
+ t.apply();
+ }
+}
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/core/java/android/view/InsetsSourceControl.aidl
similarity index 90%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to core/java/android/view/InsetsSourceControl.aidl
index 31d1339..755bf45 100644
--- a/core/java/android/service/intelligence/SnapshotData.aidl
+++ b/core/java/android/view/InsetsSourceControl.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.view;
-parcelable SnapshotData;
+parcelable InsetsSourceControl;
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
new file mode 100644
index 0000000..9383e6c
--- /dev/null
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+/**
+ * Represents a parcelable object to allow controlling a single {@link InsetsSource}.
+ * @hide
+ */
+public class InsetsSourceControl implements Parcelable {
+
+ private final @InternalInsetType int mType;
+ private final SurfaceControl mLeash;
+
+ public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash) {
+ mType = type;
+ mLeash = leash;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+
+ public InsetsSourceControl(Parcel in) {
+ mType = in.readInt();
+ mLeash = in.readParcelable(null /* loader */);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeParcelable(mLeash, 0 /* flags*/);
+ }
+
+ public static final Creator<InsetsSourceControl> CREATOR
+ = new Creator<InsetsSourceControl>() {
+ public InsetsSourceControl createFromParcel(Parcel in) {
+ return new InsetsSourceControl(in);
+ }
+
+ public InsetsSourceControl[] newArray(int size) {
+ return new InsetsSourceControl[size];
+ }
+ };
+}
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/core/java/android/view/InsetsState.aidl
similarity index 82%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to core/java/android/view/InsetsState.aidl
index 31d1339..d02ddd1 100644
--- a/core/java/android/service/intelligence/SnapshotData.aidl
+++ b/core/java/android/view/InsetsState.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2018, The Android Open Source Project
+ * Copyright (c) 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.intelligence;
+package android.view;
-parcelable SnapshotData;
+parcelable InsetsState;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
new file mode 100644
index 0000000..689b14f
--- /dev/null
+++ b/core/java/android/view/InsetsState.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.IntDef;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsets.Type.InsetType;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holder for state of system windows that cause window insets for all other windows in the system.
+ * @hide
+ */
+public class InsetsState implements Parcelable {
+
+ /**
+ * Internal representation of inset source types. This is different from the public API in
+ * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
+ * at the same time.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "TYPE", value = {
+ TYPE_TOP_BAR,
+ TYPE_SIDE_BAR_1,
+ TYPE_SIDE_BAR_2,
+ TYPE_SIDE_BAR_3,
+ TYPE_IME
+ })
+ public @interface InternalInsetType {}
+
+ static final int FIRST_TYPE = 0;
+
+ /** Top bar. Can be status bar or caption in freeform windowing mode. */
+ public static final int TYPE_TOP_BAR = FIRST_TYPE;
+
+ /**
+ * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar
+ * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have
+ * multiple, like Android Auto.
+ */
+ public static final int TYPE_SIDE_BAR_1 = 1;
+ public static final int TYPE_SIDE_BAR_2 = 2;
+ public static final int TYPE_SIDE_BAR_3 = 3;
+
+ /** Input method window. */
+ public static final int TYPE_IME = 4;
+ static final int LAST_TYPE = TYPE_IME;
+
+ // Derived types
+
+ /** First side bar is navigation bar. */
+ public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1;
+
+ /** A shelf is the same as the navigation bar. */
+ public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
+
+ private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
+
+ public InsetsState() {
+ }
+
+ /**
+ * Calculates {@link WindowInsets} based on the current source configuration.
+ *
+ * @param frame The frame to calculate the insets relative to.
+ * @return The calculated insets.
+ */
+ public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
+ boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+ Insets systemInsets = Insets.NONE;
+ Insets maxInsets = Insets.NONE;
+ final Rect relativeFrame = new Rect(frame);
+ final Rect relativeFrameMax = new Rect(frame);
+ for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ InsetsSource source = mSources.get(type);
+ if (source == null) {
+ continue;
+ }
+ systemInsets = processSource(source, systemInsets, relativeFrame,
+ false /* ignoreVisibility */);
+
+ // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
+ // target.
+ if (source.getType() != TYPE_IME) {
+ maxInsets = processSource(source, maxInsets, relativeFrameMax,
+ true /* ignoreVisibility */);
+ }
+ }
+ return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+ alwaysConsumeNavBar, cutout);
+ }
+
+ private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
+ boolean ignoreVisibility) {
+ Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
+ insets = Insets.add(currentInsets, insets);
+ relativeFrame.inset(insets);
+ return insets;
+ }
+
+ public InsetsSource getSource(@InternalInsetType int type) {
+ return mSources.computeIfAbsent(type, InsetsSource::new);
+ }
+
+ /**
+ * Modifies the state of this class to exclude a certain type to make it ready for dispatching
+ * to the client.
+ *
+ * @param type The {@link InternalInsetType} of the source to remove
+ */
+ public void removeSource(int type) {
+ mSources.remove(type);
+ }
+
+ public void set(InsetsState other) {
+ set(other, false /* copySources */);
+ }
+
+ public void set(InsetsState other, boolean copySources) {
+ mSources.clear();
+ if (copySources) {
+ for (int i = 0; i < other.mSources.size(); i++) {
+ InsetsSource source = other.mSources.valueAt(i);
+ mSources.put(source.getType(), new InsetsSource(source));
+ }
+ } else {
+ mSources.putAll(other.mSources);
+ }
+ }
+
+ public static @InternalInsetType ArraySet<Integer> toInternalType(@InsetType int insetTypes) {
+ final ArraySet<Integer> result = new ArraySet<>();
+ if ((insetTypes & Type.TOP_BAR) != 0) {
+ result.add(TYPE_TOP_BAR);
+ }
+ if ((insetTypes & Type.SIDE_BARS) != 0) {
+ result.add(TYPE_SIDE_BAR_1);
+ result.add(TYPE_SIDE_BAR_2);
+ result.add(TYPE_SIDE_BAR_3);
+ }
+ if ((insetTypes & Type.IME) != 0) {
+ result.add(TYPE_IME);
+ }
+ return result;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "InsetsState");
+ for (int i = mSources.size() - 1; i >= 0; i--) {
+ mSources.valueAt(i).dump(prefix + " ", pw);
+ }
+ }
+
+ static String typeToString(int type) {
+ switch (type) {
+ case TYPE_TOP_BAR:
+ return "TYPE_TOP_BAR";
+ case TYPE_SIDE_BAR_1:
+ return "TYPE_SIDE_BAR_1";
+ case TYPE_SIDE_BAR_2:
+ return "TYPE_SIDE_BAR_2";
+ case TYPE_SIDE_BAR_3:
+ return "TYPE_SIDE_BAR_3";
+ case TYPE_IME:
+ return "TYPE_IME";
+ default:
+ return "TYPE_UNKNOWN";
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+
+ InsetsState state = (InsetsState) o;
+
+ if (mSources.size() != state.mSources.size()) {
+ return false;
+ }
+ for (int i = mSources.size() - 1; i >= 0; i--) {
+ InsetsSource source = mSources.valueAt(i);
+ InsetsSource otherSource = state.mSources.get(source.getType());
+ if (otherSource == null) {
+ return false;
+ }
+ if (!otherSource.equals(source)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mSources.hashCode();
+ }
+
+ public InsetsState(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSources.size());
+ for (int i = 0; i < mSources.size(); i++) {
+ dest.writeParcelable(mSources.valueAt(i), 0 /* flags */);
+ }
+ }
+
+ public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
+
+ public InsetsState createFromParcel(Parcel in) {
+ return new InsetsState(in);
+ }
+
+ public InsetsState[] newArray(int size) {
+ return new InsetsState[size];
+ }
+ };
+
+ public void readFromParcel(Parcel in) {
+ mSources.clear();
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final InsetsSource source = in.readParcelable(null /* loader */);
+ mSources.put(source.getType(), source);
+ }
+ }
+}
+
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0739516..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 @@
}
}
- /**
- * Whether this key is a media key, which can be send to apps that are
- * interested in media key events.
- *
- * @hide
- */
- public static final boolean isMediaKey(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 a7a5024..ab01085 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -104,6 +104,8 @@
int flags, int mask);
private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
int l, int t, int r, int b);
+ private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
+ float cornerRadius);
private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
int layerStack);
@@ -375,9 +377,13 @@
* Construct a new {@link SurfaceControl} with the set parameters.
*/
public SurfaceControl build() {
- if (mWidth <= 0 || mHeight <= 0) {
+ if (mWidth < 0 || mHeight < 0) {
throw new IllegalArgumentException(
- "width and height must be set");
+ "width and height must be positive or unset");
+ }
+ if ((mWidth > 0 || mHeight > 0) && (isColorLayerSet() || isContainerLayerSet())) {
+ throw new IllegalArgumentException(
+ "Only buffer layers can set a valid buffer size.");
}
return new SurfaceControl(mSession, mName, mWidth, mHeight, mFormat,
mFlags, mParent, mWindowType, mOwnerUid);
@@ -399,8 +405,8 @@
* @param width The buffer width in pixels.
* @param height The buffer height in pixels.
*/
- public Builder setSize(int width, int height) {
- if (width <= 0 || height <= 0) {
+ public Builder setBufferSize(int width, int height) {
+ if (width < 0 || height < 0) {
throw new IllegalArgumentException(
"width and height must be positive");
}
@@ -533,6 +539,10 @@
return this;
}
+ private boolean isColorLayerSet() {
+ return (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+ }
+
/**
* Indicates whether a 'ContainerLayer' is to be constructed.
*
@@ -550,6 +560,10 @@
return this;
}
+ private boolean isContainerLayerSet() {
+ return (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER;
+ }
+
/**
* Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}.
*
@@ -869,10 +883,10 @@
}
}
- public void setSize(int w, int h) {
+ public void setBufferSize(int w, int h) {
checkNotReleased();
synchronized(SurfaceControl.class) {
- sGlobalTransaction.setSize(this, w, h);
+ sGlobalTransaction.setBufferSize(this, w, h);
}
}
@@ -994,6 +1008,18 @@
}
}
+ /**
+ * Sets the corner radius of a {@link SurfaceControl}.
+ *
+ * @param cornerRadius Corner radius in pixels.
+ */
+ public void setCornerRadius(float cornerRadius) {
+ checkNotReleased();
+ synchronized (SurfaceControl.class) {
+ sGlobalTransaction.setCornerRadius(this, cornerRadius);
+ }
+ }
+
public void setLayerStack(int layerStack) {
checkNotReleased();
synchronized(SurfaceControl.class) {
@@ -1427,7 +1453,7 @@
}
@UnsupportedAppUsage
- public Transaction setSize(SurfaceControl sc, int w, int h) {
+ public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
sc.checkNotReleased();
mResizedSurfaces.put(sc, new Point(w, h));
nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
@@ -1517,6 +1543,20 @@
return this;
}
+ /**
+ * Sets the corner radius of a {@link SurfaceControl}.
+ * @param sc SurfaceControl
+ * @param cornerRadius Corner radius in pixels.
+ * @return Itself.
+ */
+ @UnsupportedAppUsage
+ public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
+ sc.checkNotReleased();
+ nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius);
+
+ return this;
+ }
+
@UnsupportedAppUsage
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
sc.checkNotReleased();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2b68ec0..797d1c5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -557,7 +557,7 @@
name,
(mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
new SurfaceControl.Builder(mSurfaceSession)
- .setSize(mSurfaceWidth, mSurfaceHeight)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setFormat(mFormat)
.setFlags(mSurfaceFlags));
} else if (mSurfaceControl == null) {
@@ -595,10 +595,14 @@
mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
0.0f, 0.0f,
mScreenRect.height() / (float) mSurfaceHeight);
+ // Set a window crop when creating the surface or changing its size to
+ // crop the buffer to the surface size since the buffer producer may
+ // use SCALING_MODE_SCALE and submit a larger size than the surface
+ // size.
+ mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
}
if (sizeChanged && !creating) {
- mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
- mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
+ mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
}
} finally {
SurfaceControl.closeTransaction();
@@ -1122,6 +1126,13 @@
}
};
+ /**
+ * @hide
+ */
+ public SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
class SurfaceControlWithBackground extends SurfaceControl {
SurfaceControl mBackgroundControl;
private boolean mOpaque = true;
@@ -1133,6 +1144,8 @@
mBackgroundControl = b.setName("Background for -" + name)
.setFormat(OPAQUE)
+ // Unset the buffer size of the background color layer.
+ .setBufferSize(0, 0)
.setColorLayer(true)
.build();
mOpaque = opaque;
@@ -1158,9 +1171,9 @@
}
@Override
- public void setSize(int w, int h) {
- super.setSize(w, h);
- mBackgroundControl.setSize(w, h);
+ public void setBufferSize(int w, int h) {
+ super.setBufferSize(w, h);
+ // The background surface is a color layer so we do not set a size.
}
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5f1336f..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.IntelligenceManager;
+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;
@@ -3373,9 +3376,8 @@
*
* |-------|-------|-------|-------|
* 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED
- * 1 PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
* |-------|-------|-------|-------|
*/
@@ -3396,28 +3398,14 @@
* Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
* should be called.
*
- * The idea is to call notifyNodeAdded() after the view is layout and visible, then call
- * notifyNodeRemoved() when it's gone (without known when it was removed from the parent).
- *
- * TODO(b/111276913): the current algortighm could probably be optimized and some of them
- * removed
+ * The idea is to call notifyAppeared() after the view is layout and visible, then call
+ * notifyDisappeared() when it's gone (without known when it was removed from the parent).
*/
- private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT = 0x10;
- private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED = 0x20;
- private static final int PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE = 0x40;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
/* End of masks for mPrivateFlags4 */
- private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED = 1;
- private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED = 0;
-
- @IntDef(flag = true, prefix = { "CONTENT_CAPTURE_NOTIFICATION_TYPE_" }, value = {
- CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED,
- CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED
- })
- @Retention(RetentionPolicy.SOURCE)
- private @interface ContentCaptureNotificationType {}
-
/** @hide */
protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
/** @hide */
@@ -3993,7 +3981,7 @@
*
* @see #getParent()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected ViewParent mParent;
/**
@@ -4174,7 +4162,7 @@
float mAlpha = 1f;
/**
- * The opacity of the view as manipulated by the Fade transition. This is a hidden
+ * The opacity of the view as manipulated by the Fade transition. This is a
* property only used by transitions, which is composited with the other alpha
* values to calculate the final visual alpha value.
*/
@@ -4199,7 +4187,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
@@ -4207,7 +4195,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
@@ -4215,7 +4203,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
@@ -4223,7 +4211,7 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mBottom;
/**
@@ -4714,7 +4702,7 @@
* of this view to at least this amount.
*/
@ViewDebug.ExportedProperty(category = "measurement")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMinHeight;
/**
@@ -4722,7 +4710,7 @@
* of this view to at least this amount.
*/
@ViewDebug.ExportedProperty(category = "measurement")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMinWidth;
/**
@@ -4910,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"),
@@ -6531,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)
@@ -7597,7 +7603,9 @@
*
* {@see #setAccessibilityPaneTitle}.
*/
- @Nullable public CharSequence getAccessibilityPaneTitle() {
+ @InspectableProperty
+ @Nullable
+ public CharSequence getAccessibilityPaneTitle() {
return mAccessibilityPaneTitle;
}
@@ -8152,6 +8160,9 @@
* the user, and the activity rendering the view is enabled for Content Capture) is laid out and
* is visible.
*
+ * <p>The populated structure is then passed to the service through
+ * {@link ContentCaptureManager#notifyViewAppeared(ViewStructure)}.
+ *
* <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
* <ul>
* <li>{@link ViewStructure#setChildCount(int)}
@@ -8165,16 +8176,9 @@
* <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
* <li>{@link ViewStructure#setDataIsSensitive(boolean)}
* </ul>
- *
- * @return whether the IntelligenceService should be notified that the view was added (through
- * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view
- * hierarchy. Most views should return {@code true} here, but views that contains virtual
- * hierarchy might opt to return {@code false} and notify the manager independently, as the
- * virtual views are rendered.
*/
- public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+ public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
- return true;
}
/** @hide */
@@ -8599,6 +8603,7 @@
* @attr ref android.R.styleable#View_autofillHints
*/
@ViewDebug.ExportedProperty()
+ @InspectableProperty
@Nullable public String[] getAutofillHints() {
return mAutofillHints;
}
@@ -8643,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;
@@ -8827,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
@@ -8934,68 +8957,68 @@
}
/**
- * Helper used to notify the {@link IntelligenceManager} when the view is removed or
+ * Helper used to notify the {@link ContentCaptureManager} when the view is removed or
* added, based on whether it's laid out and visible, and without knowing if the parent removed
* it from the view hierarchy.
+ *
+ * <p>This method is called from many places (visibility changed, view laid out, view attached
+ * or detached to/from window, etc...) and hence must contain the logic to call the manager, as
+ * described below:
+ *
+ * <ol>
+ * <li>It should only be called when content capture is enabled for the view.
+ * <li>It must call viewAppeared() before viewDisappeared()
+ * <li>viewAppearead() can only be called when the view is visible and laidout
+ * <li>It should not call the same event twice.
+ * </ol>
*/
- // TODO(b/111276913): make sure the current algorithm covers all cases. For example, it should
- // probably be called every time notifyEnterOrExitForAutoFillIfNeeded() is called as well.
- private void notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- @ContentCaptureNotificationType int type) {
- if (type != CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
- && type != CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
- // Sanity check so it does not screw up the flags
- Log.wtf(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded(): "
- + "invalid type " + type + " for " + this);
- return;
- }
+ private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+ // First check if context has client, so it saves a service lookup when it doesn't
+ if (!mContext.isContentCaptureSupported()) return;
+ // Then check if it's enabled in the context...
+ final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+ if (cm == null || !cm.isContentCaptureEnabled()) return;
+
+ // ... and finally at the view level
+ // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
if (!isImportantForContentCapture()) return;
- final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
- if (im == null || !im.isContentCaptureEnabled()) return;
-
- // Make sure event is notified just once, and reset the
- // PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE flag
- boolean ignoreNotification = false;
- if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
- == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- ignoreNotification = true;
- } else {
- mPrivateFlags4 |= PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
+ if (appeared) {
+ if (!isLaidOut() || !isVisibleToUser()
+ || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+ + isLaidOut() + ", visible=" + isVisibleToUser()
+ + ": alreadyNotifiedAppeared="
+ + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0));
+ }
+ return;
}
+ // All good: notify the manager...
+ final ViewStructure structure = cm.newViewStructure(this);
+ onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ cm.notifyViewAppeared(structure);
+ // ...and set the flags
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
} else {
- if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
- == CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
- ignoreNotification = true;
- } else {
- mPrivateFlags4 &= ~PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
+ if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
+ || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this
+ + ": notifiedAppeared="
+ + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+ + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+ & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+ }
+ return;
}
- }
- if (ignoreNotification) {
- if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
- // TODO(b/111276913): remove this log statement if the algorithm is not improved
- // (right now it's called too many times when the activity is stopped and/or views
- // disappear
- Log.v(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded("
- + type + "): ignoring repeated notification on " + this);
- }
- return;
- }
-
- if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- final ViewStructure structure = im.newViewStructure(this);
- boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
- if (notifyMgr) {
- im.notifyViewAppeared(structure);
- }
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
- } else {
- if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
- return; // skip initial notification
- }
- im.notifyViewDisappeared(getAutofillId());
+ // All good: notify the manager...
+ cm.notifyViewDisappeared(getAutofillId());
+ // ...and set the flags
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
}
}
@@ -9638,6 +9661,7 @@
* @attr ref android.R.styleable#View_contentDescription
*/
@ViewDebug.ExportedProperty(category = "accessibility")
+ @InspectableProperty
public CharSequence getContentDescription() {
return mContentDescription;
}
@@ -9718,6 +9742,7 @@
*
* @see #setAccessibilityTraversalBefore(int)
*/
+ @InspectableProperty
public int getAccessibilityTraversalBefore() {
return mAccessibilityTraversalBeforeId;
}
@@ -9762,6 +9787,7 @@
*
* @see #setAccessibilityTraversalAfter(int)
*/
+ @InspectableProperty
public int getAccessibilityTraversalAfter() {
return mAccessibilityTraversalAfterId;
}
@@ -9773,6 +9799,7 @@
* @return The labeled view id.
*/
@ViewDebug.ExportedProperty(category = "accessibility")
+ @InspectableProperty
public int getLabelFor() {
return mLabelForId;
}
@@ -9836,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;
}
@@ -9860,6 +9888,7 @@
*
* @attr ref android.R.styleable#View_isScrollContainer
*/
+ @InspectableProperty(name = "isScrollContainer")
public boolean isScrollContainer() {
return (mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0;
}
@@ -9915,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;
}
@@ -9960,6 +9994,7 @@
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
+ @InspectableProperty
public boolean getKeepScreenOn() {
return (mViewFlags & KEEP_SCREEN_ON) != 0;
}
@@ -9984,6 +10019,7 @@
*
* @attr ref android.R.styleable#View_nextFocusLeft
*/
+ @InspectableProperty(name = "nextFocusLeft")
public int getNextFocusLeftId() {
return mNextFocusLeftId;
}
@@ -10005,6 +10041,7 @@
*
* @attr ref android.R.styleable#View_nextFocusRight
*/
+ @InspectableProperty(name = "nextFocusRight")
public int getNextFocusRightId() {
return mNextFocusRightId;
}
@@ -10026,6 +10063,7 @@
*
* @attr ref android.R.styleable#View_nextFocusUp
*/
+ @InspectableProperty(name = "nextFocusUp")
public int getNextFocusUpId() {
return mNextFocusUpId;
}
@@ -10047,6 +10085,7 @@
*
* @attr ref android.R.styleable#View_nextFocusDown
*/
+ @InspectableProperty(name = "nextFocusDown")
public int getNextFocusDownId() {
return mNextFocusDownId;
}
@@ -10068,6 +10107,7 @@
*
* @attr ref android.R.styleable#View_nextFocusForward
*/
+ @InspectableProperty(name = "nextFocusForward")
public int getNextFocusForwardId() {
return mNextFocusForwardId;
}
@@ -10090,6 +10130,7 @@
*
* @attr ref android.R.styleable#View_nextClusterForward
*/
+ @InspectableProperty(name = "nextClusterForward")
public int getNextClusterForwardId() {
return mNextClusterForwardId;
}
@@ -10360,6 +10401,20 @@
}
/**
+ * Retrieves the single {@link WindowInsetsController} of the window this view is attached to.
+ *
+ * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
+ * a window.
+ * @hide pending unhide
+ */
+ public @Nullable WindowInsetsController getWindowInsetsController() {
+ if (mAttachInfo != null) {
+ return mAttachInfo.mViewRootImpl.getInsetsController();
+ }
+ return null;
+ }
+
+ /**
* @hide Compute the insets that should be consumed by this view and the ones
* that should propagate to those under it.
*
@@ -10442,6 +10497,7 @@
* @see #setSystemUiVisibility(int)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean getFitsSystemWindows() {
return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
}
@@ -10503,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;
@@ -10526,6 +10587,7 @@
* @return True if this view is enabled, false otherwise.
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isEnabled() {
return (mViewFlags & ENABLED_MASK) == ENABLED;
}
@@ -10695,6 +10757,7 @@
* @attr ref android.R.styleable#View_soundEffectsEnabled
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isSoundEffectsEnabled() {
return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
}
@@ -10724,6 +10787,7 @@
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isHapticFeedbackEnabled() {
return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
}
@@ -10746,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;
@@ -10799,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;
@@ -10985,6 +11059,7 @@
* @attr ref android.R.styleable#View_clickable
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isClickable() {
return (mViewFlags & CLICKABLE) == CLICKABLE;
}
@@ -11012,6 +11087,7 @@
* @see #setLongClickable(boolean)
* @attr ref android.R.styleable#View_longClickable
*/
+ @InspectableProperty
public boolean isLongClickable() {
return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
}
@@ -11037,6 +11113,7 @@
* @see #setContextClickable(boolean)
* @attr ref android.R.styleable#View_contextClickable
*/
+ @InspectableProperty
public boolean isContextClickable() {
return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
}
@@ -11116,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;
}
@@ -11131,6 +11209,7 @@
* @see #setAssistBlocked(boolean)
* @attr ref android.R.styleable#View_assistBlocked
*/
+ @InspectableProperty
public boolean isAssistBlocked() {
return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0;
}
@@ -11168,6 +11247,7 @@
* @see #setSaveEnabled(boolean)
* @attr ref android.R.styleable#View_saveEnabled
*/
+ @InspectableProperty
public boolean isSaveEnabled() {
return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED;
}
@@ -11203,6 +11283,7 @@
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean getFilterTouchesWhenObscured() {
return (mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0;
}
@@ -11275,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;
@@ -11289,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);
}
@@ -11300,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;
}
@@ -11328,6 +11416,7 @@
*
* @attr ref android.R.styleable#View_accessibilityHeading
*/
+ @InspectableProperty
public boolean isAccessibilityHeading() {
return (mPrivateFlags3 & PFLAG3_ACCESSIBILITY_HEADING) != 0;
}
@@ -11382,6 +11471,7 @@
* @attr ref android.R.styleable#View_keyboardNavigationCluster
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public final boolean isKeyboardNavigationCluster() {
return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
}
@@ -11491,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;
}
@@ -11604,6 +11695,7 @@
* @attr ref android.R.styleable#View_defaultFocusHighlightEnabled
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public final boolean getDefaultFocusHighlightEnabled() {
return mDefaultFocusHighlightEnabled;
}
@@ -11822,6 +11914,7 @@
*
* @return True if this View is accessibility focused.
*/
+ @InspectableProperty(hasAttributeId = false)
public boolean isAccessibilityFocused() {
return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0;
}
@@ -12148,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;
@@ -12200,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;
@@ -12902,6 +13007,7 @@
public void dispatchStartTemporaryDetach() {
mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
notifyEnterOrExitForAutoFillIfNeeded(false);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
onStartTemporaryDetach();
}
@@ -12928,6 +13034,7 @@
notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
/**
@@ -13509,9 +13616,8 @@
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
}
}
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(isVisible
- ? CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
- : CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
}
/**
@@ -15046,6 +15152,7 @@
*
* @return The left edge of the displayed part of your view, in pixels.
*/
+ @InspectableProperty
public final int getScrollX() {
return mScrollX;
}
@@ -15057,6 +15164,7 @@
*
* @return The top edge of the displayed part of your view, in pixels.
*/
+ @InspectableProperty
public final int getScrollY() {
return mScrollY;
}
@@ -15293,6 +15401,7 @@
* @return The degrees of rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getRotation() {
return mRenderNode.getRotation();
}
@@ -15333,6 +15442,7 @@
* @return The degrees of Y rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getRotationY() {
return mRenderNode.getRotationY();
}
@@ -15377,6 +15487,7 @@
* @return The degrees of X rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getRotationX() {
return mRenderNode.getRotationX();
}
@@ -15422,6 +15533,7 @@
* @return The scaling factor.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getScaleX() {
return mRenderNode.getScaleX();
}
@@ -15459,6 +15571,7 @@
* @return The scaling factor.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getScaleY() {
return mRenderNode.getScaleY();
}
@@ -15498,6 +15611,7 @@
* @attr ref android.R.styleable#View_transformPivotX
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty(name = "transformPivotX")
public float getPivotX() {
return mRenderNode.getPivotX();
}
@@ -15540,6 +15654,7 @@
* @attr ref android.R.styleable#View_transformPivotY
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty(name = "transformPivotY")
public float getPivotY() {
return mRenderNode.getPivotY();
}
@@ -15598,6 +15713,7 @@
* @return The opacity of the view.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
}
@@ -15751,15 +15867,12 @@
}
/**
- * This property is hidden and intended only for use by the Fade transition, which
- * animates it to produce a visual translucency that does not side-effect (or get
- * affected by) the real alpha property. This value is composited with the other
- * alpha value (and the AlphaAnimation value, when that is present) to produce
- * a final visual translucency result, which is what is passed into the DisplayList.
- *
- * @hide
+ * This property is intended only for use by the Fade transition, which animates it
+ * to produce a visual translucency that does not side-effect (or get affected by)
+ * the real alpha property. This value is composited with the other alpha value
+ * (and the AlphaAnimation value, when that is present) to produce a final visual
+ * translucency result, which is what is passed into the DisplayList.
*/
- @UnsupportedAppUsage
public void setTransitionAlpha(float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mTransitionAlpha != alpha) {
@@ -15782,16 +15895,13 @@
}
/**
- * This property is hidden and intended only for use by the Fade transition, which
- * animates it to produce a visual translucency that does not side-effect (or get
- * affected by) the real alpha property. This value is composited with the other
- * alpha value (and the AlphaAnimation value, when that is present) to produce
- * a final visual translucency result, which is what is passed into the DisplayList.
- *
- * @hide
+ * This property is intended only for use by the Fade transition, which animates
+ * it to produce a visual translucency that does not side-effect (or get affected
+ * by) the real alpha property. This value is composited with the other alpha
+ * value (and the AlphaAnimation value, when that is present) to produce a final
+ * visual translucency result, which is what is passed into the DisplayList.
*/
@ViewDebug.ExportedProperty(category = "drawing")
- @UnsupportedAppUsage
public float getTransitionAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mTransitionAlpha : 1;
}
@@ -15826,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();
}
@@ -16197,6 +16308,7 @@
* @return The base depth position of the view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getElevation() {
return mRenderNode.getElevation();
}
@@ -16225,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();
}
@@ -16259,6 +16372,7 @@
* in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getTranslationY() {
return mRenderNode.getTranslationY();
}
@@ -16290,6 +16404,7 @@
* @return The depth of this view relative to its elevation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public float getTranslationZ() {
return mRenderNode.getTranslationZ();
}
@@ -16310,9 +16425,17 @@
}
}
- /** @hide */
- @UnsupportedAppUsage
- public void setAnimationMatrix(Matrix matrix) {
+ /**
+ * Changes the transformation matrix on the view. This is used in animation frameworks,
+ * such as {@link android.transition.Transition}. When the animation finishes, the matrix
+ * should be cleared by calling this method with <code>null</code> as the matrix parameter.
+ * Application developers should use transformation methods like {@link #setRotation(float)},
+ * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}}
+ * and {@link #setTranslationY(float)} (float)}} instead.
+ *
+ * @param matrix The matrix, null indicates that the matrix should be cleared.
+ */
+ public void setAnimationMatrix(@Nullable Matrix matrix) {
invalidateViewProperty(true, false);
mRenderNode.setAnimationMatrix(matrix);
invalidateViewProperty(false, true);
@@ -16326,6 +16449,7 @@
* @return StateListAnimator or null if it does not exists
* @see #setStateListAnimator(android.animation.StateListAnimator)
*/
+ @InspectableProperty
public StateListAnimator getStateListAnimator() {
return mStateListAnimator;
}
@@ -16439,6 +16563,7 @@
*
* @see #setOutlineProvider(ViewOutlineProvider)
*/
+ @InspectableProperty
public ViewOutlineProvider getOutlineProvider() {
return mOutlineProvider;
}
@@ -16515,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();
}
@@ -16543,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();
}
@@ -17722,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.
@@ -17885,6 +18042,7 @@
*
* @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
*/
+ @InspectableProperty
public int getScrollBarDefaultDelayBeforeFade() {
return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
mScrollCache.scrollBarDefaultDelayBeforeFade;
@@ -17909,6 +18067,7 @@
*
* @attr ref android.R.styleable#View_scrollbarFadeDuration
*/
+ @InspectableProperty
public int getScrollBarFadeDuration() {
return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
mScrollCache.scrollBarFadeDuration;
@@ -17933,6 +18092,7 @@
*
* @attr ref android.R.styleable#View_scrollbarSize
*/
+ @InspectableProperty
public int getScrollBarSize() {
return mScrollCache == null ? ViewConfiguration.get(mContext).getScaledScrollBarSize() :
mScrollCache.scrollBarSize;
@@ -17992,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;
@@ -19082,6 +19248,7 @@
needGlobalAttributesUpdate(false);
notifyEnterOrExitForAutoFillIfNeeded(true);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
@UnsupportedAppUsage
@@ -19131,8 +19298,7 @@
}
notifyEnterOrExitForAutoFillIfNeeded(false);
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
}
/**
@@ -19413,6 +19579,7 @@
* @see #getDrawableState()
* @see #setDuplicateParentStateEnabled(boolean)
*/
+ @InspectableProperty(name = "duplicateParentState")
public boolean isDuplicateParentStateEnabled() {
return (mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE;
}
@@ -19454,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");
@@ -19538,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;
}
@@ -21438,12 +21611,7 @@
notifyEnterOrExitForAutoFillIfNeeded(true);
}
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
- && (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT) == 0) {
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED);
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT;
- }
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
private boolean hasParentWantsFocus() {
@@ -21487,7 +21655,7 @@
* previous ones
* {@hide}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
@@ -21552,11 +21720,19 @@
}
/**
- * Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}.
- * @hide
+ * Assign a size and position to this view.
+ *
+ * This method is meant to be used in animations only as it applies this position and size
+ * for the view only temporary and it can be changed back at any time by the layout.
+ *
+ * @param left Left position, relative to parent
+ * @param top Top position, relative to parent
+ * @param right Right position, relative to parent
+ * @param bottom Bottom position, relative to parent
+ *
+ * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int)
*/
- @UnsupportedAppUsage
- public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ public final void setLeftTopRightBottom(int left, int top, int right, int bottom) {
setFrame(left, top, right, bottom);
}
@@ -22311,6 +22487,7 @@
*
* @attr ref android.R.styleable#View_background
*/
+ @InspectableProperty
public Drawable getBackground() {
return mBackground;
}
@@ -22346,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;
@@ -22382,6 +22560,7 @@
* @see #setBackgroundTintMode(PorterDuff.Mode)
*/
@Nullable
+ @InspectableProperty
public PorterDuff.Mode getBackgroundTintMode() {
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
}
@@ -22417,6 +22596,7 @@
*
* @see #onDrawForeground(Canvas)
*/
+ @InspectableProperty
public Drawable getForeground() {
return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
}
@@ -22495,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;
@@ -22562,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
@@ -22601,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
@@ -22845,6 +23028,7 @@
*
* @return the top padding in pixels
*/
+ @InspectableProperty
public int getPaddingTop() {
return mPaddingTop;
}
@@ -22856,6 +23040,7 @@
*
* @return the bottom padding in pixels
*/
+ @InspectableProperty
public int getPaddingBottom() {
return mPaddingBottom;
}
@@ -22867,6 +23052,7 @@
*
* @return the left padding in pixels
*/
+ @InspectableProperty
public int getPaddingLeft() {
if (!isPaddingResolved()) {
resolvePadding();
@@ -22896,6 +23082,7 @@
*
* @return the right padding in pixels
*/
+ @InspectableProperty
public int getPaddingRight() {
if (!isPaddingResolved()) {
resolvePadding();
@@ -23018,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;
}
@@ -23061,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;
}
@@ -23157,26 +23346,24 @@
* Modifies the input matrix such that it maps view-local coordinates to
* on-screen coordinates.
*
- * @param m input matrix to modify
- * @hide
+ * @param matrix input matrix to modify
*/
- @UnsupportedAppUsage
- public void transformMatrixToGlobal(Matrix m) {
+ public void transformMatrixToGlobal(Matrix matrix) {
final ViewParent parent = mParent;
if (parent instanceof View) {
final View vp = (View) parent;
- vp.transformMatrixToGlobal(m);
- m.preTranslate(-vp.mScrollX, -vp.mScrollY);
+ vp.transformMatrixToGlobal(matrix);
+ matrix.preTranslate(-vp.mScrollX, -vp.mScrollY);
} else if (parent instanceof ViewRootImpl) {
final ViewRootImpl vr = (ViewRootImpl) parent;
- vr.transformMatrixToGlobal(m);
- m.preTranslate(0, -vr.mCurScrollY);
+ vr.transformMatrixToGlobal(matrix);
+ matrix.preTranslate(0, -vr.mCurScrollY);
}
- m.preTranslate(mLeft, mTop);
+ matrix.preTranslate(mLeft, mTop);
if (!hasIdentityMatrix()) {
- m.preConcat(getMatrix());
+ matrix.preConcat(getMatrix());
}
}
@@ -23184,26 +23371,24 @@
* Modifies the input matrix such that it maps on-screen coordinates to
* view-local coordinates.
*
- * @param m input matrix to modify
- * @hide
+ * @param matrix input matrix to modify
*/
- @UnsupportedAppUsage
- public void transformMatrixToLocal(Matrix m) {
+ public void transformMatrixToLocal(Matrix matrix) {
final ViewParent parent = mParent;
if (parent instanceof View) {
final View vp = (View) parent;
- vp.transformMatrixToLocal(m);
- m.postTranslate(vp.mScrollX, vp.mScrollY);
+ vp.transformMatrixToLocal(matrix);
+ matrix.postTranslate(vp.mScrollX, vp.mScrollY);
} else if (parent instanceof ViewRootImpl) {
final ViewRootImpl vr = (ViewRootImpl) parent;
- vr.transformMatrixToLocal(m);
- m.postTranslate(0, vr.mCurScrollY);
+ vr.transformMatrixToLocal(matrix);
+ matrix.postTranslate(0, vr.mCurScrollY);
}
- m.postTranslate(-mLeft, -mTop);
+ matrix.postTranslate(-mLeft, -mTop);
if (!hasIdentityMatrix()) {
- m.postConcat(getInverseMatrix());
+ matrix.postConcat(getInverseMatrix());
}
}
@@ -23563,11 +23748,22 @@
*/
@IdRes
@ViewDebug.CapturedViewProperty
+ @InspectableProperty
public int getId() {
return mID;
}
/**
+ * Get the identifier used for this view by the drawing system.
+ *
+ * @see RenderNode#getUniqueId()
+ * @return A long that uniquely identifies this view's drawing component
+ */
+ public long getUniqueDrawingId() {
+ return mRenderNode.getUniqueId();
+ }
+
+ /**
* Returns this view's tag.
*
* @return the Object stored in this view as a tag, or {@code null} if not
@@ -23577,6 +23773,7 @@
* @see #getTag(int)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public Object getTag() {
return mTag;
}
@@ -23773,6 +23970,7 @@
* if baseline alignment is not supported
*/
@ViewDebug.ExportedProperty(category = "layout")
+ @InspectableProperty
public int getBaseline() {
return -1;
}
@@ -24143,6 +24341,7 @@
*
* @attr ref android.R.styleable#View_minHeight
*/
+ @InspectableProperty(name = "minHeight")
public int getMinimumHeight() {
return mMinHeight;
}
@@ -24173,6 +24372,7 @@
*
* @attr ref android.R.styleable#View_minWidth
*/
+ @InspectableProperty(name = "minWidth")
public int getMinimumWidth() {
return mMinWidth;
}
@@ -24772,7 +24972,7 @@
final SurfaceSession session = new SurfaceSession(root.mSurface);
final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
.setName("drag surface")
- .setSize(shadowSize.x, shadowSize.y)
+ .setBufferSize(shadowSize.x, shadowSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
final Surface surface = new Surface();
@@ -25192,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;
}
@@ -25250,6 +25455,7 @@
*
* @see #setNestedScrollingEnabled(boolean)
*/
+ @InspectableProperty
public boolean isNestedScrollingEnabled() {
return (mPrivateFlags3 & PFLAG3_NESTED_SCROLLING_ENABLED) ==
PFLAG3_NESTED_SCROLLING_ENABLED;
@@ -25579,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;
@@ -25646,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;
}
@@ -25817,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() {
@@ -25883,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) >>
@@ -26115,6 +26357,7 @@
/**
* Gets the pointer icon for the current view.
*/
+ @InspectableProperty
public PointerIcon getPointerIcon() {
return mPointerIcon;
}
@@ -26665,6 +26908,7 @@
* if no name has been given.
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public String getTransitionName() {
return mTransitionName;
}
@@ -28270,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/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 767cd33..d03d97e 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -393,7 +393,7 @@
case HAS_PERMANENT_MENU_KEY_AUTODETECT: {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
- sHasPermanentMenuKey = !wm.hasNavigationBar();
+ sHasPermanentMenuKey = !wm.hasNavigationBar(context.getDisplayId());
sHasPermanentMenuKeySet = true;
} catch (RemoteException ex) {
sHasPermanentMenuKey = false;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1e91aa8..741510e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7042,10 +7042,7 @@
* suppression is disabled with a later call to suppressLayout(false).
* When layout suppression is disabled, a requestLayout() call is sent
* if layout() was attempted while layout was being suppressed.
- *
- * @hide
*/
- @UnsupportedAppUsage
public void suppressLayout(boolean suppress) {
mSuppressLayout = suppress;
if (!suppress) {
@@ -7061,8 +7058,6 @@
* suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
*
* @return true if layout calls are currently suppressed, false otherwise.
- *
- * @hide
*/
public boolean isLayoutSuppressed() {
return mSuppressLayout;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 484c6f3..cb47886 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -161,6 +161,24 @@
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
+ * If set to true, the view system will switch from using rectangles retrieved from window to
+ * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
+ * directly from the full configuration, enabling richer information about the insets state, as
+ * well as new APIs to control it frame-by-frame, and synchronize animations with it.
+ * <p>
+ * Only switch this to true once the new insets system is productionized and the old APIs are
+ * fully migrated over.
+ */
+ private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
+
+ /**
+ * @see #USE_NEW_INSETS_PROPERTY
+ * @hide
+ */
+ public static final boolean USE_NEW_INSETS =
+ SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+
+ /**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
@@ -432,6 +450,8 @@
boolean mAdded;
boolean mAddedTouchMode;
+ final Rect mTmpFrame = new Rect();
+
// These are accessed by multiple threads.
final Rect mWinFrame; // frame given by window manager.
@@ -444,6 +464,7 @@
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
+ private InsetsState mPendingInsets = new InsetsState();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -531,6 +552,8 @@
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private final InsetsController mInsetsController = new InsetsController();
+
static final class SystemUiVisibilityInfo {
int seq;
int globalVisibility;
@@ -797,9 +820,11 @@
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
+ getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
- mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
+ mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
+ mInsetsController.getState());
+ setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
@@ -826,6 +851,7 @@
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
+ mPendingInsets = mInsetsController.getState();
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1473,31 +1499,22 @@
mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName("Bounds for - " + getTitle().toString())
- .setSize(mWidth, mHeight)
.build();
- setBoundsSurfaceSizeAndCrop();
+ setBoundsSurfaceCrop();
mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer)
.show(mBoundsSurfaceControl)
.apply();
mBoundsSurface.copyFrom(mBoundsSurfaceControl);
}
- private void setBoundsSurfaceSizeAndCrop() {
+ private void setBoundsSurfaceCrop() {
// mWinFrame is already adjusted for surface insets. So offset it and use it as
// the cropping bounds.
mTempBoundsRect.set(mWinFrame);
mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
mWindowAttributes.surfaceInsets.top);
mTransaction.setWindowCrop(mBoundsSurfaceControl, mTempBoundsRect);
-
- // Expand the bounds by the surface insets to get the size of surface.
- mTempBoundsRect.inset(-mWindowAttributes.surfaceInsets.left,
- -mWindowAttributes.surfaceInsets.top,
- -mWindowAttributes.surfaceInsets.right,
- -mWindowAttributes.surfaceInsets.bottom);
- mTransaction.setSize(mBoundsSurfaceControl, mTempBoundsRect.width(),
- mTempBoundsRect.height());
}
/**
@@ -1506,7 +1523,7 @@
*/
private void updateBoundsSurface() {
if (mBoundsSurfaceControl != null && mSurface.isValid()) {
- setBoundsSurfaceSizeAndCrop();
+ setBoundsSurfaceCrop();
mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
mSurface, mSurface.getNextFrameNumber())
.apply();
@@ -1780,7 +1797,8 @@
Rect stableInsets = mDispatchStableInsets;
DisplayCutout displayCutout = mDispatchDisplayCutout;
// For dispatch we preserve old logic, but for direct requests from Views we allow to
- // immediately use pending insets.
+ // immediately use pending insets. This is such that getRootWindowInsets returns the
+ // result from the layout hint before we ran a traversal shortly after adding a window.
if (!forceConstruct
&& (!mPendingContentInsets.equals(contentInsets) ||
!mPendingStableInsets.equals(stableInsets) ||
@@ -1797,10 +1815,16 @@
}
contentInsets = ensureInsetsNonNegative(contentInsets, "content");
stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
- mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets,
- mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ if (USE_NEW_INSETS) {
+ mLastWindowInsets = mInsetsController.calculateInsets(
+ mContext.getResources().getConfiguration().isScreenRound(),
+ mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ } else {
+ mLastWindowInsets = new WindowInsets(contentInsets,
+ null /* windowDecorInsets */, stableInsets,
+ mContext.getResources().getConfiguration().isScreenRound(),
+ mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ }
}
return mLastWindowInsets;
}
@@ -1828,6 +1852,10 @@
host.dispatchApplyWindowInsets(insets);
}
+ InsetsController getInsetsController() {
+ return mInsetsController;
+ }
+
private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
return lp.type == TYPE_STATUS_BAR_PANEL
|| lp.type == TYPE_INPUT_METHOD
@@ -1916,7 +1944,6 @@
// PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
- mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
@@ -2000,6 +2027,9 @@
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;
@@ -2193,6 +2223,8 @@
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;
@@ -2230,6 +2262,10 @@
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
+ if (insetsStateChanged) {
+ mInsetsController.setState(mPendingInsets);
+ contentInsetsChanged = true;
+ }
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -2675,7 +2711,6 @@
}
private void maybeHandleWindowMove(Rect frame) {
-
// TODO: Well, we are checking whether the frame has changed similarly
// to how this is done for the insets. This is however incorrect since
// the insets and the frame are translated. For example, the old frame
@@ -4180,6 +4215,8 @@
private final static int MSG_UPDATE_POINTER_ICON = 27;
private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
private final static int MSG_DRAW_FINISHED = 29;
+ private final static int MSG_INSETS_CHANGED = 30;
+ private final static int MSG_INSETS_CONTROL_CHANGED = 31;
final class ViewRootHandler extends Handler {
@Override
@@ -4235,6 +4272,8 @@
return "MSG_POINTER_CAPTURE_CHANGED";
case MSG_DRAW_FINISHED:
return "MSG_DRAW_FINISHED";
+ case MSG_INSETS_CHANGED:
+ return "MSG_INSETS_CHANGED";
}
return super.getMessageName(message);
}
@@ -4315,7 +4354,7 @@
|| !mPendingVisibleInsets.equals(args.arg3)
|| !mPendingOutsets.equals(args.arg7);
- mWinFrame.set((Rect) args.arg1);
+ setFrame((Rect) args.arg1);
mPendingOverscanInsets.set((Rect) args.arg5);
mPendingContentInsets.set((Rect) args.arg2);
mPendingStableInsets.set((Rect) args.arg6);
@@ -4338,16 +4377,36 @@
requestLayout();
}
break;
+ case MSG_INSETS_CHANGED:
+ mPendingInsets = (InsetsState) msg.obj;
+
+ // TODO: Full traversal not needed here.
+ if (USE_NEW_INSETS) {
+ requestLayout();
+ }
+ 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();
+ }
+ break;
+ }
case MSG_WINDOW_MOVED:
if (mAdded) {
final int w = mWinFrame.width();
final int h = mWinFrame.height();
final int l = msg.arg1;
final int t = msg.arg2;
- mWinFrame.left = l;
- mWinFrame.right = l + w;
- mWinFrame.top = t;
- mWinFrame.bottom = t + h;
+ mTmpFrame.left = l;
+ mTmpFrame.right = l + w;
+ mTmpFrame.top = t;
+ mTmpFrame.bottom = t + h;
+ setFrame(mTmpFrame);
mPendingBackDropFrame.set(mWinFrame);
maybeHandleWindowMove(mWinFrame);
@@ -6733,9 +6792,9 @@
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
- mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
+ mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurface);
+ mPendingMergedConfiguration, mSurface, mPendingInsets);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6745,15 +6804,22 @@
}
if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+ mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
+ setFrame(mTmpFrame);
+
return relayoutResult;
}
+ private void setFrame(Rect frame) {
+ mWinFrame.set(frame);
+ mInsetsController.onFrameChanged(frame);
+ }
+
/**
* {@inheritDoc}
*/
@@ -6856,6 +6922,8 @@
mChoreographer.dump(prefix, writer);
+ mInsetsController.dump(prefix, writer);
+
writer.print(prefix); writer.println("View Hierarchy:");
dumpViewHierarchy(innerPrefix, writer, mView);
}
@@ -7064,6 +7132,18 @@
mHandler.sendMessage(msg);
}
+ private void dispatchInsetsChanged(InsetsState insetsState) {
+ mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
+ }
+
+ private void dispatchInsetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = insetsState;
+ args.arg2 = activeControls;
+ mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget();
+ }
+
public void dispatchMoved(int newX, int newY) {
if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
if (mTranslator != null) {
@@ -8127,6 +8207,23 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchInsetsChanged(insetsState);
+ }
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls);
+ }
+ }
+
+ @Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c1e94d8..58ab817 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2410,4 +2410,11 @@
public boolean isCloseOnSwipeEnabled() {
return mCloseOnSwipeEnabled;
}
+
+ /**
+ * @return The {@link WindowInsetsController} associated with this window
+ * @see View#getWindowInsetsController()
+ * @hide pending unhide
+ */
+ public abstract @NonNull WindowInsetsController getInsetsController();
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index a8debbd..572d331 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -18,13 +18,17 @@
package android.view;
import android.annotation.NonNull;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.view.inputmethod.InputMethod;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -807,4 +811,69 @@
mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
}
}
+
+ /**
+ * Class that defines different types of sources causing window insets.
+ * @hide pending unhide
+ */
+ public static final class Type {
+
+ static final int TOP_BAR = 0x1;
+ static final int IME = 0x2;
+ static final int SIDE_BARS = 0x4;
+ static final int WINDOW_DECOR = 0x8;
+
+ private Type() {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = { TOP_BAR, IME, SIDE_BARS, WINDOW_DECOR })
+ public @interface InsetType {
+ }
+
+ /**
+ * @return An inset type representing the top bar of a window, which can be the status
+ * bar on handheld-like devices as well as a caption bar.
+ */
+ public static @InsetType int topBar() {
+ return TOP_BAR;
+ }
+
+ /**
+ * @return An inset type representing the window of an {@link InputMethod}.
+ */
+ public static @InsetType int ime() {
+ return IME;
+ }
+
+ /**
+ * @return An inset type representing any system bars that are not {@link #topBar()}.
+ */
+ public static @InsetType int sideBars() {
+ return SIDE_BARS;
+ }
+
+ /**
+ * @return An inset type representing decor that is being app-controlled.
+ */
+ public static @InsetType int windowDecor() {
+ return WINDOW_DECOR;
+ }
+
+ /**
+ * @return All system bars. Includes {@link #topBar()} as well as {@link #sideBars()}, but
+ * not {@link #ime()}.
+ */
+ public static @InsetType int systemBars() {
+ return TOP_BAR | SIDE_BARS;
+ }
+
+ /**
+ * @return All inset types combined.
+ */
+ public static @InsetType int all() {
+ return 0xFFFFFFFF;
+ }
+ }
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
new file mode 100644
index 0000000..7be5f2e
--- /dev/null
+++ b/core/java/android/view/WindowInsetsController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.view.WindowInsets.Type.InsetType;
+
+/**
+ * Interface to control windows that generate insets.
+ *
+ * TODO Needs more information and examples once the API is more baked.
+ * @hide pending unhide
+ */
+public interface WindowInsetsController {
+
+ /**
+ * Makes a set of windows that cause insets appear on screen.
+ * <p>
+ * Note that if the window currently doesn't have control over a certain type, it will apply the
+ * change as soon as the window gains control. The app can listen to the event by observing
+ * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
+ * {@link WindowInsets}.
+ *
+ * @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
+ * would like to make appear on screen.
+ */
+ void show(@InsetType int types);
+
+ /**
+ * Makes a set of windows causing insets disappear.
+ * <p>
+ * Note that if the window currently doesn't have control over a certain type, it will apply the
+ * change as soon as the window gains control. The app can listen to the event by observing
+ * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
+ * {@link WindowInsets}.
+ *
+ * @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
+ * would like to make disappear.
+ */
+ void hide(@InsetType int types);
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8d8a370..45c3651 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1752,15 +1752,6 @@
public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
/**
- * If this flag is set on the window, window manager will acquire a sleep token that puts
- * all activities to sleep as long as this window is visible. When this flag is set, the
- * window needs to occlude all activity windows.
- * @hide
- */
- @RequiresPermission(permission.DEVICE_POWER)
- public static final int PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN = 0x00200000;
-
- /**
* Flag to indicate that this window should be considered a screen decoration similar to the
* nav bar and status bar. This will cause this window to affect the window insets reported
* to other windows when it is visible.
@@ -1872,10 +1863,6 @@
equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
name = "IS_ROUNDED_CORNERS_OVERLAY"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
- equals = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
- name = "ACQUIRES_SLEEP_TOKEN"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
name = "IS_SCREEN_DECOR"),
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 260e938..16bafe2 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -44,6 +44,7 @@
int PRESENCE_EXTERNAL = 1 << 1;
// Navigation bar position values
+ int NAV_BAR_INVALID = -1;
int NAV_BAR_LEFT = 1 << 0;
int NAV_BAR_RIGHT = 1 << 1;
int NAV_BAR_BOTTOM = 1 << 2;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d4c7069..9227249 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -202,6 +202,14 @@
public static final String EXTRA_RESTORE_SESSION_TOKEN =
"android.view.autofill.extra.RESTORE_SESSION_TOKEN";
+ /**
+ * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
+ "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
+
private static final String SESSION_ID_TAG = "android:sessionId";
private static final String STATE_TAG = "android:state";
private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -370,6 +378,9 @@
private Cleaner mServiceClientCleaner;
@GuardedBy("mLock")
+ private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
+
+ @GuardedBy("mLock")
private AutofillCallback mCallback;
private final Context mContext;
@@ -1664,6 +1675,8 @@
final IAutoFillManager service = mService;
final IAutoFillManagerClient serviceClient = mServiceClient;
mServiceClientCleaner = Cleaner.create(this, () -> {
+ // TODO(b/111330312): call service to also remove reference to
+ // mAugmentedAutofillServiceClient
try {
service.removeClient(serviceClient, userId);
} catch (RemoteException e) {
@@ -1808,6 +1821,7 @@
if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
mServiceClient = null;
+ mAugmentedAutofillServiceClient = null;
if (mServiceClientCleaner != null) {
mServiceClientCleaner.clean();
mServiceClientCleaner = null;
@@ -2054,6 +2068,29 @@
}
}
+ /**
+ * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
+ *
+ * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
+ * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
+ */
+ private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillServiceClient == null) {
+ mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
+ }
+ final Bundle resultData = new Bundle();
+ resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
+ mAugmentedAutofillServiceClient.asBinder());
+
+ try {
+ result.send(0, resultData);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
+ }
+ }
+ }
+
/** @hide */
public void requestHideFillUi() {
requestHideFillUi(mIdShownFillUi, true);
@@ -2801,7 +2838,7 @@
private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
private final WeakReference<AutofillManager> mAfm;
- AutofillManagerClient(AutofillManager autofillManager) {
+ private AutofillManagerClient(AutofillManager autofillManager) {
mAfm = new WeakReference<>(autofillManager);
}
@@ -2904,6 +2941,50 @@
afm.post(() -> afm.setSessionFinished(newState));
}
}
+
+ @Override
+ public void getAugmentedAutofillClient(IResultReceiver result) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.getAugmentedAutofillClient(result));
+ }
+ }
+ }
+
+ private static final class AugmentedAutofillManagerClient
+ extends IAugmentedAutofillManagerClient.Stub {
+ private final WeakReference<AutofillManager> mAfm;
+
+ private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
+ mAfm = new WeakReference<>(autofillManager);
+ }
+
+ @Override
+ public Rect getViewCoordinates(@NonNull AutofillId id) {
+ // TODO(b/111330312): use handler / callback?
+ final AutofillManager afm = mAfm.get();
+ if (afm == null) return null;
+
+ final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
+ // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
+ // fix (for example, take system status bar height into account) logic below
+ final int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
+ location[1] + view.getHeight());
+ if (sVerbose) {
+ Log.v(TAG, "Coordinates for " + id + ": " + rect);
+ }
+ return rect;
+ }
+
+ @Override
+ public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.autofill(sessionId, ids, values));
+ }
+ }
}
/**
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
new file mode 100644
index 0000000..67cd0bf
--- /dev/null
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import java.util.List;
+
+import android.graphics.Rect;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Object running in the application process and responsible to provide the functionalities
+ * required by an Augmented Autofill service.
+ *
+ * @hide
+ */
+interface IAugmentedAutofillManagerClient {
+ Rect getViewCoordinates(in AutofillId id);
+ void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 0ff7a0b..63394b4 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -27,6 +27,8 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.KeyEvent;
+import com.android.internal.os.IResultReceiver;
+
/**
* Object running in the application process and responsible for autofilling it.
*
@@ -93,8 +95,18 @@
/**
* Marks the state of the session as finished.
+ *
* @param newState STATE_FINISHED (because the autofill service returned a null
* FillResponse) or STATE_UNKNOWN (because the session was removed).
*/
void setSessionFinished(int newState);
+
+ /**
+ * Gets a reference to the binder object that can be used by the Augmented Autofill service.
+ *
+ * @param receiver, whose AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT extra will contain
+ * the reference.
+ */
+ void getAugmentedAutofillClient(in IResultReceiver result);
+
}
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 befcb55..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.IntelligenceManager#FLAG_USER_INPUT}.
+ * {@link android.view.contentcapture.ContentCaptureManager#FLAG_USER_INPUT}.
*/
public int getFlags() {
return mFlags;
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
new file mode 100644
index 0000000..48831da
--- /dev/null
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+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;
+
+/*
+ * NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread.
+ *
+ * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning
+ * of every method.
+ */
+/**
+ * TODO(b/111276913): add javadocs / implement
+ */
+@SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)
+public final class 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;
+ private static final boolean DEBUG = true; // STOPSHIP if not set to false
+
+ /**
+ * Used to indicate that a text change was caused by user input (for example, through IME).
+ */
+ //TODO(b/111276913): link to notifyTextChanged() method once available
+ public static final int FLAG_USER_INPUT = 0x1;
+
+ /**
+ * Initial state, when there is no session.
+ *
+ * @hide
+ */
+ public static final int STATE_UNKNOWN = 0;
+
+ /**
+ * Service's startSession() was called, but server didn't confirm it was created yet.
+ *
+ * @hide
+ */
+ public static final int STATE_WAITING_FOR_SERVER = 1;
+
+ /**
+ * Session is active.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 2;
+
+ /**
+ * Session is disabled.
+ *
+ * @hide
+ */
+ public static final int STATE_DISABLED = 3;
+
+ private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
+
+ /**
+ * Maximum number of events that are buffered before sent to the app.
+ */
+ // TODO(b/111276913): use settings
+ private static final int MAX_BUFFER_SIZE = 100;
+
+ @NonNull
+ private final AtomicBoolean mDisabled = new AtomicBoolean();
+
+ @NonNull
+ private final Context mContext;
+
+ @Nullable
+ private final IContentCaptureManager mService;
+
+ @Nullable
+ private String mId;
+
+ private int mState = STATE_UNKNOWN;
+
+ @Nullable
+ private IBinder mApplicationToken;
+
+ @Nullable
+ private ComponentName mComponentName;
+
+ /**
+ * List of events held to be sent as a batch.
+ */
+ @Nullable
+ private ArrayList<ContentCaptureEvent> mEvents;
+
+ // TODO(b/111276913): use UI Thread directly (as calls are one-way) or a shared thread / handler
+ // held at the Application level
+ private final Handler mHandler;
+
+ /** @hide */
+ 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());
+ }
+ mService = service;
+ // TODO(b/111276913): use an existing bg thread instead...
+ final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
+ bgThread.start();
+ mHandler = Handler.createAsync(bgThread.getLooper());
+ }
+
+ /** @hide */
+ public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (!isContentCaptureEnabled()) return;
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleStartSession, this,
+ token, componentName));
+ }
+
+ private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (mState != STATE_UNKNOWN) {
+ // TODO(b/111276913): revisit this scenario
+ Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
+ + getStateAsString(mState));
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mId = UUID.randomUUID().toString();
+ mApplicationToken = token;
+ mComponentName = componentName;
+
+ if (VERBOSE) {
+ Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+ + getActivityDebugName() + ", id=" + mId);
+ }
+ final int flags = 0; // TODO(b/111276913): get proper flags
+
+ try {
+ mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
+ mId, flags, new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ handleSessionStarted(resultCode);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
+ + e);
+ }
+ }
+
+ private void handleSessionStarted(int resultCode) {
+ mState = resultCode;
+ mDisabled.set(mState == STATE_DISABLED);
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityStarted() result: code=" + resultCode + ", id=" + mId
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
+ }
+ }
+
+ private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ if (mEvents == null) {
+ if (VERBOSE) {
+ Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+ }
+ mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+ }
+ mEvents.add(event);
+ final int numberEvents = mEvents.size();
+ if (numberEvents < MAX_BUFFER_SIZE && !forceFlush) {
+ // Buffering events, return right away...
+ return;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ // Callback from startSession hasn't been called yet - typically happens on system
+ // apps that are started before the system service
+ // TODO(b/111276913): try to ignore session while system is not ready / boot
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (VERBOSE) {
+ Log.v(TAG, "Closing session for " + getActivityDebugName()
+ + " after " + numberEvents + " delayed events and state "
+ + getStateAsString(mState));
+ }
+ handleResetState();
+ // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // when it's launched again
+ return;
+ }
+
+ if (mId == null) {
+ // Sanity check - should not happen
+ Log.wtf(TAG, "null session id for " + getActivityDebugName());
+ return;
+ }
+
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+ }
+ mService.sendEvents(mContext.getUserId(), mId, mEvents);
+ // TODO(b/111276913): decide whether we should clear or set it to null, as each has
+ // its own advantages: clearing will save extra allocations while the session is
+ // active, while setting to null would save memory if there's no more event coming.
+ mEvents.clear();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+ + ": " + e);
+ }
+ }
+
+ /**
+ * Used for intermediate events (i.e, other than created and destroyed).
+ *
+ * @hide
+ */
+ public void onActivityLifecycleEvent(@EventType int type) {
+ if (!isContentCaptureEnabled()) return;
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugName()
+ + ": " + ContentCaptureEvent.getTypeAsString(type));
+ }
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(type), /* forceFlush= */ true));
+ }
+
+ /** @hide */
+ public void onActivityDestroyed() {
+ if (!isContentCaptureEnabled()) return;
+
+ //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+ // id) and send it to the cache of batched commands
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
+ + ", mId=" + mId);
+ }
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleFinishSession, this));
+ }
+
+ private void handleFinishSession() {
+ //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
+ // to system_server, so it's ok to call both in sequence here. But once we split
+ // them so the events are sent directly to the service, we need to make sure they're
+ // sent in order.
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "Finishing session " + mId + " with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getActivityDebugName());
+ }
+
+ mService.finishSession(mContext.getUserId(), mId, mEvents);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finishing session " + mId + " for " + getActivityDebugName()
+ + ": " + e);
+ } finally {
+ handleResetState();
+ }
+ }
+
+ private void handleResetState() {
+ mState = STATE_UNKNOWN;
+ mId = null;
+ mApplicationToken = null;
+ mComponentName = null;
+ mEvents = null;
+ }
+
+ /**
+ * Notifies the Intelligence Service that a node has been added to the view structure.
+ *
+ * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+ * automatically by the Android System for views that return {@code true} on
+ * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
+ *
+ * @param node node that has been added.
+ */
+ public void notifyViewAppeared(@NonNull ViewStructure node) {
+ Preconditions.checkNotNull(node);
+ if (!isContentCaptureEnabled()) return;
+
+ if (!(node instanceof ViewNode.ViewStructureImpl)) {
+ throw new IllegalArgumentException("Invalid node class: " + node.getClass());
+ }
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_APPEARED)
+ .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
+ /* forceFlush= */ false));
+ }
+
+ /**
+ * Notifies the Intelligence Service that a node has been removed from the view structure.
+ *
+ * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+ * automatically by the Android System for standard views.
+ *
+ * @param id id of the node that has been removed.
+ */
+ public void notifyViewDisappeared(@NonNull AutofillId id) {
+ Preconditions.checkNotNull(id);
+ if (!isContentCaptureEnabled()) return;
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+ /* forceFlush= */ false));
+ }
+
+ /**
+ * Notifies the Intelligence Service that the value of a text node has been changed.
+ *
+ * @param id of the node.
+ * @param text new text.
+ * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
+ * changed by the user (for example, through the keyboard).
+ */
+ public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ Preconditions.checkNotNull(id);
+
+ if (!isContentCaptureEnabled()) return;
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+ .setText(text), /* forceFlush= */ false));
+ }
+
+ /**
+ * Creates a {@link ViewStructure} for a "standard" view.
+ *
+ * @hide
+ */
+ @NonNull
+ public ViewStructure newViewStructure(@NonNull View view) {
+ return new ViewNode.ViewStructureImpl(view);
+ }
+
+ /**
+ * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
+ * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
+ *
+ * @param parentId id of the virtual view parent (it can be obtained by calling
+ * {@link ViewStructure#getAutofillId()} on the parent).
+ * @param virtualId id of the virtual child, relative to the parent.
+ *
+ * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
+ */
+ @NonNull
+ public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+ return new ViewNode.ViewStructureImpl(parentId, virtualId);
+ }
+
+ /**
+ * Returns the component name of the system service that is consuming the captured events for
+ * the current user.
+ */
+ @Nullable
+ public ComponentName getServiceComponentName() {
+ //TODO(b/111276913): implement
+ return null;
+ }
+
+ /**
+ * Checks whether content capture is enabled for this activity.
+ */
+ public boolean isContentCaptureEnabled() {
+ return mService != null && !mDisabled.get();
+ }
+
+ /**
+ * Called by apps to explicitly enable or disable content capture.
+ *
+ * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
+ * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
+ */
+ public void setContentCaptureEnabled(boolean enabled) {
+ //TODO(b/111276913): implement
+ }
+
+ /** @hide */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.println("IntelligenceManager");
+ final String prefix2 = prefix + " ";
+ pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+ if (mService != null) {
+ pw.print(prefix2); pw.print("mService: "); pw.println(mService);
+ }
+ pw.print(prefix2); pw.print("mDisabled: "); pw.println(mDisabled.get());
+ pw.print(prefix2); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+ if (mId != null) {
+ pw.print(prefix2); pw.print("id: "); pw.println(mId);
+ }
+ pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
+ pw.print(getStateAsString(mState)); pw.println(")");
+ if (mApplicationToken != null) {
+ pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
+ }
+ if (mComponentName != null) {
+ pw.print(prefix2); pw.print("component name: ");
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null) {
+ final int numberEvents = mEvents.size();
+ pw.print(prefix2); pw.print("buffered events: "); pw.print(numberEvents);
+ pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+ if (VERBOSE && numberEvents > 0) {
+ final String prefix3 = prefix2 + " ";
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
+ private String getActivityDebugName() {
+ return mComponentName == null ? mContext.getPackageName()
+ : mComponentName.flattenToShortString();
+ }
+
+ @NonNull
+ private static String getStateAsString(int state) {
+ switch (state) {
+ case STATE_UNKNOWN:
+ return "UNKNOWN";
+ case STATE_WAITING_FOR_SERVER:
+ return "WAITING_FOR_SERVER";
+ case STATE_ACTIVE:
+ return "ACTIVE";
+ case STATE_DISABLED:
+ return "DISABLED";
+ default:
+ return "INVALID:" + state;
+ }
+ }
+}
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 98%
rename from core/java/android/view/intelligence/ViewNode.java
rename to core/java/android/view/contentcapture/ViewNode.java
index cc78e6b..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;
@@ -238,6 +238,8 @@
@Override
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mText = text;
// TODO(b/111276913): implement or move to superclass
}
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 2f128de..0000000
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ /dev/null
@@ -1,48 +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}
- */
-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);
-
- /**
- * Sends a batch of events
- */
- void sendEvents(int userId, in InteractionSessionId sessionId,
- in List<ContentCaptureEvent> events);
-}
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
deleted file mode 100644
index dfa52d9..0000000
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ /dev/null
@@ -1,554 +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 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 com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-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 com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * TODO(b/111276913): add javadocs / implement
- */
-@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
-public final class IntelligenceManager {
-
- private static final String TAG = "IntelligenceManager";
-
- // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
- private static final boolean VERBOSE = false;
- private static final boolean DEBUG = true; // STOPSHIP if not set to false
-
- /**
- * Used to indicate that a text change was caused by user input (for example, through IME).
- */
- //TODO(b/111276913): link to notifyTextChanged() method once available
- public static final int FLAG_USER_INPUT = 0x1;
-
- /**
- * Initial state, when there is no session.
- *
- * @hide
- */
- public static final int STATE_UNKNOWN = 0;
-
- /**
- * Service's startSession() was called, but server didn't confirm it was created yet.
- *
- * @hide
- */
- public static final int STATE_WAITING_FOR_SERVER = 1;
-
- /**
- * Session is active.
- *
- * @hide
- */
- public static final int STATE_ACTIVE = 2;
-
- /**
- * Session is disabled.
- *
- * @hide
- */
- public static final int STATE_DISABLED = 3;
-
- private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
-
- /**
- * Maximum number of events that are delayed for an app.
- *
- * <p>If the session is not started after the limit is reached, it's discarded.
- */
- private static final int MAX_DELAYED_SIZE = 20;
-
- private final Context mContext;
-
- @Nullable
- private final IIntelligenceManager mService;
-
- private final Object mLock = new Object();
-
- @Nullable
- @GuardedBy("mLock")
- private InteractionSessionId mId;
-
- @GuardedBy("mLock")
- private int mState = STATE_UNKNOWN;
-
- @GuardedBy("mLock")
- private IBinder mApplicationToken;
-
- // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
- // AutofillClient
- @GuardedBy("mLock")
- private ComponentName mComponentName;
-
- // TODO(b/111276913): create using maximum batch size as capacity
- /**
- * List of events held to be sent as a batch.
- */
- @GuardedBy("mLock")
- private final ArrayList<ContentCaptureEvent> mEvents = new ArrayList<>();
-
- private final Handler mHandler;
-
- /** @hide */
- public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
- mContext = Preconditions.checkNotNull(context, "context cannot be null");
- mService = service;
-
- // TODO(b/111276913): use an existing bg thread instead...
- final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
- bgThread.start();
- mHandler = Handler.createAsync(bgThread.getLooper());
- }
-
- /** @hide */
- public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
- if (!isContentCaptureEnabled()) return;
-
- synchronized (mLock) {
- if (mState != STATE_UNKNOWN) {
- // TODO(b/111276913): revisit this scenario
- Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
- + getStateAsString(mState));
- return;
- }
- mState = STATE_WAITING_FOR_SERVER;
- mId = new InteractionSessionId();
- mApplicationToken = token;
- mComponentName = componentName;
-
- if (VERBOSE) {
- Log.v(TAG, "onActivityCreated(): token=" + token + ", act="
- + getActivityDebugNameLocked() + ", id=" + mId);
- }
- final int flags = 0; // TODO(b/111276913): get proper flags
-
- try {
- mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
- mId, flags, new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData)
- throws RemoteException {
- synchronized (mLock) {
- mState = resultCode;
- if (VERBOSE) {
- Log.v(TAG, "onActivityStarted() result: code=" + resultCode
- + ", id=" + mId
- + ", state=" + getStateAsString(mState));
- }
- }
- }
- });
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
- // calling right away
- private void sendEvent(@NonNull ContentCaptureEvent event) {
- mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this, event));
- }
-
- private void handleSendEvent(@NonNull ContentCaptureEvent event) {
-
- //TODO(b/111276913): make a copy and don't use lock
- synchronized (mLock) {
- mEvents.add(event);
- final int numberEvents = mEvents.size();
- if (mState != STATE_ACTIVE) {
- if (numberEvents >= MAX_DELAYED_SIZE) {
- // Typically happens on system apps that are started before the system service
- // is ready (like com.android.settings/.FallbackHome)
- //TODO(b/111276913): try to ignore session while system is not ready / boot
- // not complete instead. Similarly, the manager service should return right away
- // when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugNameLocked()
- + " after " + numberEvents + " delayed events and state "
- + getStateAsString(mState));
- }
- // TODO(b/111276913): blacklist activity / use special flag to indicate that
- // when it's launched again
- resetStateLocked();
- return;
- }
-
- if (VERBOSE) {
- Log.v(TAG, "Delaying " + numberEvents + " events for "
- + getActivityDebugNameLocked() + " while on state "
- + getStateAsString(mState));
- }
- return;
- }
-
- if (mId == null) {
- // Sanity check - should not happen
- Log.wtf(TAG, "null session id for " + mComponentName);
- return;
- }
-
- //TODO(b/111276913): right now we're sending sending right away (unless not ready), but
- // we should hold the events and flush later.
- try {
- if (DEBUG) {
- Log.d(TAG, "Sending " + numberEvents + " event(s) for "
- + getActivityDebugNameLocked());
- }
- mService.sendEvents(mContext.getUserId(), mId, mEvents);
- mEvents.clear();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Used for intermediate events (i.e, other than created and destroyed).
- *
- * @hide
- */
- public void onActivityLifecycleEvent(@EventType int type) {
- if (!isContentCaptureEnabled()) return;
- if (VERBOSE) {
- Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugNameLocked()
- + ": " + ContentCaptureEvent.getTypeAsString(type));
- }
- sendEvent(new ContentCaptureEvent(type));
- }
-
- /** @hide */
- public void onActivityDestroyed() {
- if (!isContentCaptureEnabled()) return;
-
- synchronized (mLock) {
- //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
- // id) and send it to the cache of batched commands
-
- if (VERBOSE) {
- Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
- + ", mId=" + mId);
- }
-
- try {
- mService.finishSession(mContext.getUserId(), mId);
- resetStateLocked();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- @GuardedBy("mLock")
- private void resetStateLocked() {
- mState = STATE_UNKNOWN;
- mId = null;
- mApplicationToken = null;
- mComponentName = null;
- mEvents.clear();
- }
-
- /**
- * Notifies the Intelligence Service that a node has been added to the view structure.
- *
- * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
- * automatically by the Android System for views that return {@code true} on
- * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
- *
- * @param node node that has been added.
- */
- public void notifyViewAppeared(@NonNull ViewStructure node) {
- Preconditions.checkNotNull(node);
- if (!isContentCaptureEnabled()) return;
-
- if (!(node instanceof ViewNode.ViewStructureImpl)) {
- throw new IllegalArgumentException("Invalid node class: " + node.getClass());
- }
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_APPEARED)
- .setViewNode(((ViewNode.ViewStructureImpl) node).mNode));
- }
-
- /**
- * Notifies the Intelligence Service that a node has been removed from the view structure.
- *
- * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
- * automatically by the Android System for standard views.
- *
- * @param id id of the node that has been removed.
- */
- public void notifyViewDisappeared(@NonNull AutofillId id) {
- Preconditions.checkNotNull(id);
- if (!isContentCaptureEnabled()) return;
-
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id));
- }
-
- /**
- * Notifies the Intelligence Service that the value of a text node has been changed.
- *
- * @param id of the node.
- * @param text new text.
- * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
- * changed by the user (for example, through the keyboard).
- */
- public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
- int flags) {
- Preconditions.checkNotNull(id);
- if (!isContentCaptureEnabled()) return;
-
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
- .setText(text));
- }
-
- /**
- * Creates a {@link ViewStructure} for a "standard" view.
- *
- * @hide
- */
- @NonNull
- public ViewStructure newViewStructure(@NonNull View view) {
- return new ViewNode.ViewStructureImpl(view);
- }
-
- /**
- * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
- * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
- *
- * @param parentId id of the virtual view parent (it can be obtained by calling
- * {@link ViewStructure#getAutofillId()} on the parent).
- * @param virtualId id of the virtual child, relative to the parent.
- *
- * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
- */
- @NonNull
- public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
- return new ViewNode.ViewStructureImpl(parentId, virtualId);
- }
-
- /**
- * Returns the component name of the {@code android.service.intelligence.IntelligenceService}
- * that is enabled for the current user.
- */
- @Nullable
- public ComponentName getIntelligenceServiceComponentName() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /**
- * Checks whether content capture is enabled for this activity.
- */
- public boolean isContentCaptureEnabled() {
- //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
- // service, or if service is not set
- // (and probably renamign to isEnabledLocked()
- return mService != null && mState != STATE_DISABLED;
- }
-
- /**
- * Called by apps to explicitly enabled or disable content capture.
- *
- * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
- * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
- */
- public void setContentCaptureEnabled(boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Called by the the service {@link android.service.intelligence.IntelligenceService}
- * to define whether content capture should be enabled for activities with such
- * {@link android.content.ComponentName}.
- *
- * <p>Useful to blacklist a particular activity.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- public void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
- boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Called by the the service {@link android.service.intelligence.IntelligenceService}
- * to explicitly limit 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")));}
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- public void setContentCaptureWhitelist(@Nullable List<String> packages,
- @Nullable List<ComponentName> activities) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Called by the the service {@link android.service.intelligence.IntelligenceService}
- * to define 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.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- public void setPackageContentCaptureEnabled(@NonNull String packageName, boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Gets the activities where content capture was disabled by
- * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- @NonNull
- public Set<ComponentName> getContentCaptureDisabledActivities() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /**
- * Gets the apps where content capture was disabled by
- * {@link #setPackageContentCaptureEnabled(String, boolean)}.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- @NonNull
- public Set<String> getContentCaptureDisabledPackages() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /** @hide */
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.println("IntelligenceManager");
- final String prefix2 = prefix + " ";
- synchronized (mLock) {
- pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
- pw.print(prefix2); pw.print("mService: "); pw.println(mService);
- pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
- pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
- pw.print(prefix2); pw.print("id: "); pw.println(mId);
- pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
- pw.print(getStateAsString(mState)); pw.println(")");
- pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
- pw.print(prefix2); pw.print("component name: ");
- pw.println(mComponentName == null ? "null" : mComponentName.flattenToShortString());
- final int numberEvents = mEvents.size();
- pw.print(prefix2); pw.print("batched events: "); pw.println(numberEvents);
- if (numberEvents > 0) {
- for (int i = 0; i < numberEvents; i++) {
- final ContentCaptureEvent event = mEvents.get(i);
- pw.println(i); pw.print(": "); event.dump(pw); pw.println();
- }
-
- }
- }
- }
-
- /**
- * Gets a string that can be used to identify the activity on logging statements.
- */
- @GuardedBy("mLock")
- private String getActivityDebugNameLocked() {
- return mComponentName == null ? mContext.getPackageName()
- : mComponentName.flattenToShortString();
- }
-
- @NonNull
- private static String getStateAsString(int state) {
- switch (state) {
- case STATE_UNKNOWN:
- return "UNKNOWN";
- case STATE_WAITING_FOR_SERVER:
- return "WAITING_FOR_SERVER";
- case STATE_ACTIVE:
- return "ACTIVE";
- case STATE_DISABLED:
- return "DISABLED";
- default:
- return "INVALID:" + state;
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
new file mode 100644
index 0000000..797b861
--- /dev/null
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.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.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.app.Person;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class for action suggestions.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class ActionsSuggestionsHelper {
+ private static final int USER_LOCAL = 0;
+ private static final int FIRST_NON_LOCAL_USER = 1;
+
+ private ActionsSuggestionsHelper() {}
+
+ /**
+ * Converts the messages to a list of native messages object that the model can understand.
+ * <p>
+ * User id encoding - local user is represented as 0, Other users are numbered according to
+ * how far before they spoke last time in the conversation. For example, considering this
+ * conversation:
+ * <ul>
+ * <li> User A: xxx
+ * <li> Local user: yyy
+ * <li> User B: zzz
+ * </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> messagesWithText =
+ messages.stream()
+ .filter(message -> !TextUtils.isEmpty(message.getText()))
+ .collect(Collectors.toCollection(ArrayList::new));
+ 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(),
+ 0,
+ null)};
+ }
+
+ // Encode the messages in the reverse order, stop whenever the Person object is missing.
+ Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
+ PersonEncoder personEncoder = new PersonEncoder();
+ for (int i = size - 1; i >= 0; i--) {
+ ConversationActions.Message message = messagesWithText.get(i);
+ if (message.getAuthor() == null) {
+ break;
+ }
+ nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
+ personEncoder.encode(message.getAuthor()),
+ message.getText().toString(), 0, null));
+ }
+ return nativeMessages.toArray(
+ new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
+ }
+
+ private static final class PersonEncoder {
+ private final Map<Person, Integer> mMapping = new ArrayMap<>();
+ private int mNextUserId = FIRST_NON_LOCAL_USER;
+
+ private int encode(Person person) {
+ if (ConversationActions.Message.PERSON_USER_LOCAL.equals(person)) {
+ return USER_LOCAL;
+ }
+ Integer result = mMapping.get(person);
+ if (result == null) {
+ mMapping.put(person, mNextUserId);
+ result = mNextUserId;
+ mNextUserId++;
+ }
+ return result;
+ }
+ }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 5fcf227..1a7b911 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -345,6 +345,16 @@
/** Represents a message in the conversation. */
public static final class Message implements Parcelable {
+ /**
+ * Represents the local user.
+ *
+ * @see Builder#setAuthor(Person)
+ */
+ public static final Person PERSON_USER_LOCAL =
+ new Person.Builder()
+ .setKey("text-classifier-conversation-actions-local-user")
+ .build();
+
@Nullable
private final Person mAuthor;
@Nullable
@@ -446,7 +456,11 @@
@Nullable
private Bundle mExtras;
- /** Sets the person who composed this message. */
+ /**
+ * Sets the person who composed this message.
+ * <p>
+ * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+ */
@NonNull
public Builder setAuthor(@Nullable Person author) {
mAuthor = author;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f6c3d77..e0910c0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -105,7 +105,7 @@
/**
* @hide
*/
- static final TextClassification EMPTY = new TextClassification.Builder().build();
+ public static final TextClassification EMPTY = new TextClassification.Builder().build();
private static final String LOG_TAG = "TextClassification";
// TODO(toki): investigate a way to derive this based on device properties.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 524f709..a2536cb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -90,6 +90,11 @@
String TYPE_DATE_TIME = "datetime";
/** Flight number in IATA format. */
String TYPE_FLIGHT_NUMBER = "flight";
+ /**
+ * Word that users may be interested to look up for meaning.
+ * @hide
+ */
+ String TYPE_DICTIONARY = "dictionary";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -103,6 +108,7 @@
TYPE_DATE,
TYPE_DATE_TIME,
TYPE_FLIGHT_NUMBER,
+ TYPE_DICTIONARY
})
@interface EntityType {}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..8e14dfd 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,9 +40,9 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -269,17 +269,17 @@
final ZonedDateTime refTime = ZonedDateTime.now();
final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
? request.getEntityConfig().resolveEntityListModifications(
- getEntitiesForHints(request.getEntityConfig().getHints()))
+ getEntitiesForHints(request.getEntityConfig().getHints()))
: mSettings.getEntityListDefault();
final AnnotatorModel annotatorImpl =
getAnnotatorImpl(request.getDefaultLocales());
final AnnotatorModel.AnnotatedSpan[] annotations =
annotatorImpl.annotate(
- textString,
- new AnnotatorModel.AnnotationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- concatenateLocales(request.getDefaultLocales())));
+ textString,
+ new AnnotatorModel.AnnotationOptions(
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
+ concatenateLocales(request.getDefaultLocales())));
for (AnnotatorModel.AnnotatedSpan span : annotations) {
final AnnotatorModel.ClassificationResult[] results =
span.getClassification();
@@ -373,20 +373,13 @@
// Actions model is optional, fallback if it is not available.
return mFallback.suggestConversationActions(request);
}
- List<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayList<>();
- for (ConversationActions.Message message : request.getConversation()) {
- if (TextUtils.isEmpty(message.getText())) {
- continue;
- }
- // TODO: We need to map the Person object to user id.
- int userId = 1;
- nativeMessages.add(
- new ActionsSuggestionsModel.ConversationMessage(
- userId, message.getText().toString()));
+ ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
+ ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+ if (nativeMessages.length == 0) {
+ return mFallback.suggestConversationActions(request);
}
ActionsSuggestionsModel.Conversation nativeConversation =
- new ActionsSuggestionsModel.Conversation(nativeMessages.toArray(
- new ActionsSuggestionsModel.ConversationMessage[0]));
+ new ActionsSuggestionsModel.Conversation(nativeMessages);
ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
actionsImpl.suggestActions(nativeConversation, null);
@@ -523,10 +516,10 @@
final TextClassification.Builder builder = new TextClassification.Builder()
.setText(classifiedText);
- final int size = classifications.length;
+ final int typeCount = classifications.length;
AnnotatorModel.ClassificationResult highestScoringResult =
- size > 0 ? classifications[0] : null;
- for (int i = 0; i < size; i++) {
+ typeCount > 0 ? classifications[0] : null;
+ for (int i = 0; i < typeCount; i++) {
builder.setEntityType(classifications[i].getCollection(),
classifications[i].getScore());
if (classifications[i].getScore() > highestScoringResult.getScore()) {
@@ -534,9 +527,12 @@
}
}
+ // TODO: Make this configurable.
+ final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
boolean isPrimaryAction = true;
for (LabeledIntent labeledIntent : IntentFactory.create(
- mContext, classifiedText, referenceTime, highestScoringResult)) {
+ mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold),
+ referenceTime, highestScoringResult)) {
final RemoteAction action = labeledIntent.asRemoteAction(mContext);
if (action == null) {
continue;
@@ -558,6 +554,42 @@
return builder.setId(createId(text, start, end)).build();
}
+ private boolean isForeignText(String text, float threshold) {
+ // TODO: Revisit this algorithm.
+ try {
+ final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
+ if (langResults.length <= 0) {
+ return false;
+ }
+
+ LangIdModel.LanguageResult highestScoringResult = langResults[0];
+ for (int i = 1; i < langResults.length; i++) {
+ if (langResults[i].getScore() > highestScoringResult.getScore()) {
+ highestScoringResult = langResults[i];
+ }
+ }
+ if (highestScoringResult.getScore() < threshold) {
+ return false;
+ }
+ // TODO: Remove
+ Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
+ highestScoringResult.getLanguage(), highestScoringResult.getScore()));
+
+ final Locale detected = new Locale(highestScoringResult.getLanguage());
+ final LocaleList deviceLocales = LocaleList.getDefault();
+ final int size = deviceLocales.size();
+ for (int i = 0; i < size; i++) {
+ if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
+ return false;
+ }
+ }
+ return true;
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t);
+ }
+ return false;
+ }
+
@Override
public void dump(@NonNull IndentingPrintWriter printWriter) {
synchronized (mLock) {
@@ -608,7 +640,8 @@
/**
* Helper class to store the information from which RemoteActions are built.
*/
- private static final class LabeledIntent {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static final class LabeledIntent {
static final int DEFAULT_REQUEST_CODE = 0;
@@ -643,7 +676,8 @@
return mDescription;
}
- Intent getIntent() {
+ @VisibleForTesting
+ public Intent getIntent() {
return mIntent;
}
@@ -687,7 +721,8 @@
/**
* Creates intents based on the classification type.
*/
- static final class IntentFactory {
+ @VisibleForTesting
+ public static final class IntentFactory {
private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
@@ -698,53 +733,70 @@
public static List<LabeledIntent> create(
Context context,
String text,
+ boolean foreignText,
@Nullable Instant referenceTime,
@Nullable AnnotatorModel.ClassificationResult classification) {
final String type = classification != null
? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
- : null;
+ : "";
text = text.trim();
+ final List<LabeledIntent> actions;
switch (type) {
case TextClassifier.TYPE_EMAIL:
- return createForEmail(context, text);
+ actions = createForEmail(context, text);
+ break;
case TextClassifier.TYPE_PHONE:
- return createForPhone(context, text);
+ actions = createForPhone(context, text);
+ break;
case TextClassifier.TYPE_ADDRESS:
- return createForAddress(context, text);
+ actions = createForAddress(context, text);
+ break;
case TextClassifier.TYPE_URL:
- return createForUrl(context, text);
- case TextClassifier.TYPE_DATE:
+ actions = createForUrl(context, text);
+ break;
+ case TextClassifier.TYPE_DATE: // fall through
case TextClassifier.TYPE_DATE_TIME:
if (classification.getDatetimeResult() != null) {
final Instant parsedTime = Instant.ofEpochMilli(
classification.getDatetimeResult().getTimeMsUtc());
- return createForDatetime(context, type, referenceTime, parsedTime);
+ actions = createForDatetime(context, type, referenceTime, parsedTime);
} else {
- return new ArrayList<>();
+ actions = new ArrayList<>();
}
+ break;
case TextClassifier.TYPE_FLIGHT_NUMBER:
- return createForFlight(context, text);
+ actions = createForFlight(context, text);
+ break;
+ case TextClassifier.TYPE_DICTIONARY:
+ actions = createForDictionary(context, text);
+ break;
default:
- return new ArrayList<>();
+ actions = new ArrayList<>();
+ break;
}
+ if (foreignText) {
+ insertTranslateAction(actions, context, text);
+ }
+ return actions;
}
@NonNull
private static List<LabeledIntent> createForEmail(Context context, String text) {
- return Arrays.asList(
- new LabeledIntent(
- context.getString(com.android.internal.R.string.email),
- context.getString(com.android.internal.R.string.email_desc),
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE),
- new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- context.getString(com.android.internal.R.string.add_contact_desc),
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
- text.hashCode()));
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.email),
+ context.getString(com.android.internal.R.string.email_desc),
+ new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("mailto:%s", text))),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.add_contact),
+ context.getString(com.android.internal.R.string.add_contact_desc),
+ new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+ text.hashCode()));
+ return actions;
}
@NonNull
@@ -801,12 +853,14 @@
if (Uri.parse(text).getScheme() == null) {
text = "http://" + text;
}
- return Arrays.asList(new LabeledIntent(
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.browse),
context.getString(com.android.internal.R.string.browse_desc),
new Intent(Intent.ACTION_VIEW, Uri.parse(text))
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
LabeledIntent.DEFAULT_REQUEST_CODE));
+ return actions;
}
@NonNull
@@ -828,12 +882,14 @@
@NonNull
private static List<LabeledIntent> createForFlight(Context context, String text) {
- return Arrays.asList(new LabeledIntent(
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.view_flight),
context.getString(com.android.internal.R.string.view_flight_desc),
new Intent(Intent.ACTION_WEB_SEARCH)
.putExtra(SearchManager.QUERY, text),
text.hashCode()));
+ return actions;
}
@NonNull
@@ -864,5 +920,27 @@
parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
parsedTime.hashCode());
}
+
+ private static void insertTranslateAction(
+ List<LabeledIntent> actions, Context context, String text) {
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.translate),
+ context.getString(com.android.internal.R.string.translate_desc),
+ new Intent(Intent.ACTION_TRANSLATE)
+ // TODO: Probably better to introduce a "translate" scheme instead of
+ // using EXTRA_TEXT.
+ .putExtra(Intent.EXTRA_TEXT, text),
+ text.hashCode()));
+ }
+
+ @NonNull
+ private static List<LabeledIntent> createForDictionary(Context context, String text) {
+ return Arrays.asList(new LabeledIntent(
+ context.getString(com.android.internal.R.string.define),
+ context.getString(com.android.internal.R.string.define_desc),
+ new Intent(Intent.ACTION_DEFINE)
+ .putExtra(Intent.EXTRA_TEXT, text),
+ text.hashCode()));
+ }
}
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 02aee50..1e42c41 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -59,7 +59,7 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
- STATUS_DIFFERENT_TEXT})
+ STATUS_DIFFERENT_TEXT, STATUS_UNSUPPORTED_CHARACTER})
public @interface Status {}
/** Links were successfully applied to the text. */
@@ -74,6 +74,9 @@
/** The specified text does not match the text used to generate the links. */
public static final int STATUS_DIFFERENT_TEXT = 3;
+ /** The specified text contains unsupported characters. */
+ public static final int STATUS_UNSUPPORTED_CHARACTER = 4;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index be4c3bc..8af4233 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -107,6 +107,13 @@
Preconditions.checkNotNull(textLinks);
final String textString = text.toString();
+
+ if (Linkify.containsUnsupportedCharacters(textString)) {
+ // Do not apply links to text containing unsupported characters.
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
+ }
+
if (!textString.startsWith(textLinks.getText())) {
return TextLinks.STATUS_DIFFERENT_TEXT;
}
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/WebView.java b/core/java/android/webkit/WebView.java
index 1093719..414cb8f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -410,6 +410,9 @@
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
+ if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+ setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+ }
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
@@ -2695,8 +2698,8 @@
}
@Override
- public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
- return mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
+ public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+ mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
}
@Override
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/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index ceada07..95e7a986 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -341,10 +341,9 @@
return true; // true is the default value returned by View.isVisibleToUserForAutofill()
}
- default boolean onProvideContentCaptureStructure(
+ default void onProvideContentCaptureStructure(
@SuppressWarnings("unused") android.view.ViewStructure structure,
@SuppressWarnings("unused") int flags) {
- return false; // WebView provides virtual views and is responsible to notify manager
}
public AccessibilityNodeProvider getAccessibilityNodeProvider();
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/GridView.java b/core/java/android/widget/GridView.java
index 9ccd321..f781802 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.os.Trace;
import android.util.AttributeSet;
@@ -107,7 +108,7 @@
*/
public static final int AUTO_FIT = -1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521080)
private int mNumColumns = AUTO_FIT;
@UnsupportedAppUsage
@@ -117,7 +118,7 @@
@UnsupportedAppUsage
private int mVerticalSpacing = 0;
private int mStretchMode = STRETCH_COLUMN_WIDTH;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521079)
private int mColumnWidth;
@UnsupportedAppUsage
private int mRequestedColumnWidth;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 12cc54d..c21182c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -106,9 +106,9 @@
private boolean mHaveFrame = false;
@UnsupportedAppUsage
private boolean mAdjustViewBounds = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMaxWidth = Integer.MAX_VALUE;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMaxHeight = Integer.MAX_VALUE;
// these are applied to the drawable
@@ -1331,9 +1331,17 @@
}
}
- /** @hide */
- @UnsupportedAppUsage
- public void animateTransform(Matrix matrix) {
+ /**
+ * Applies a temporary transformation {@link Matrix} to the view's drawable when it is drawn.
+ * Allows custom scaling, translation, and perspective distortion during an animation.
+ *
+ * This method is a lightweight analogue of {@link ImageView#setImageMatrix(Matrix)} to use
+ * only during animations as this matrix will be cleared after the next drawable
+ * update or view's bounds change.
+ *
+ * @param matrix The transformation parameters in matrix form.
+ */
+ public void animateTransform(@Nullable Matrix matrix) {
if (mDrawable == null) {
return;
}
@@ -1341,6 +1349,7 @@
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
mDrawable.setBounds(0, 0, vwidth, vheight);
+ mDrawMatrix = null;
} else {
mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
if (mDrawMatrix == null) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f2e478d..15910bb 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -189,7 +189,7 @@
@ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
}, formatToHexString = true)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mGravity = Gravity.START | Gravity.TOP;
@ViewDebug.ExportedProperty(category = "measurement")
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index f4c25c3..7d02757 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -837,7 +837,7 @@
mSurfaceSession = new SurfaceSession(parentSurface);
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setFormat(PixelFormat.TRANSLUCENT)
- .setSize(mSurfaceWidth, mSurfaceHeight)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setName("magnifier surface")
.setFlags(SurfaceControl.HIDDEN)
.build();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index a28cc40..157992a 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -18,6 +18,8 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Px;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
@@ -30,6 +32,7 @@
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
import android.text.InputType;
@@ -88,6 +91,16 @@
* of the current value. Tapping on the current value allows to type in a
* desired value.
* </li>
+ * <li>
+ * If the current theme is derived from {@link android.R.style#Theme_Material}
+ * the widget presents the current value as a scrolling vertical selector with
+ * the selected value in the center and the previous and following numbers above
+ * and below, separated by a divider. The value is changed by flinging vertically.
+ * The thickness of the divider can be changed by using the
+ * {@link android.R.attr#selectionDividerHeight} attribute and the color of the
+ * divider can be changed by using the
+ * {@link android.R.attr#colorControlNormal} attribute.
+ * </li>
* </ul>
* <p>
* For an example of using this widget, see {@link android.widget.TimePicker}.
@@ -436,14 +449,14 @@
/**
* Divider for showing item to be selected while scrolling
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private final Drawable mSelectionDivider;
/**
* The height of the selection divider.
*/
- @UnsupportedAppUsage
- private final int mSelectionDividerHeight;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ private int mSelectionDividerHeight;
/**
* The current scroll state of the number picker.
@@ -1556,6 +1569,24 @@
return mSelectorIndexToStringCache.get(getValue());
}
+ /**
+ * Set the height for the divider that separates the currently selected value from the others.
+ * @param height The height to be set
+ */
+ public void setSelectionDividerHeight(@IntRange(from = 0) @Px int height) {
+ mSelectionDividerHeight = height;
+ invalidate();
+ }
+
+ /**
+ * Retrieve the height for the divider that separates the currently selected value from the
+ * others.
+ * @return The height of the divider
+ */
+ public int getSelectionDividerHeight() {
+ return mSelectionDividerHeight;
+ }
+
@Override
protected float getTopFadingEdgeStrength() {
return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 74051e2..506d615 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -204,7 +204,7 @@
private View mBaselineView = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mGravity = Gravity.START | Gravity.TOP;
private final Rect mContentBounds = new Rect();
private final Rect mSelfBounds = new Rect();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c0979fe..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 =
@@ -161,6 +169,37 @@
private static final int LAYOUT_PARAM_ACTION_TAG = 19;
private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
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.
@@ -177,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
*/
@@ -196,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.
*/
@@ -217,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;
@@ -274,23 +306,29 @@
}
/**
+ * Sets an integer tag to the view.
+ *
+ * @hide
+ */
+ public void setIntTag(int viewId, int key, int tag) {
+ addAction(new SetIntTagAction(viewId, key, tag));
+ }
+
+ /**
* Set that it is disallowed to reapply another remoteview with the same layout as this view.
* This should be done if an action is destroying the view tree of the base layout.
*
* @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;
}
/**
@@ -758,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);
@@ -819,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();
@@ -833,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;
@@ -1535,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) {
@@ -2122,6 +2164,43 @@
}
}
+ private class SetIntTagAction extends Action {
+ private final int mViewId;
+ private final int mKey;
+ private final int mTag;
+
+ SetIntTagAction(int viewId, int key, int tag) {
+ mViewId = viewId;
+ mKey = key;
+ mTag = tag;
+ }
+
+ SetIntTagAction(Parcel parcel) {
+ mViewId = parcel.readInt();
+ mKey = parcel.readInt();
+ mTag = parcel.readInt();
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mViewId);
+ dest.writeInt(mKey);
+ dest.writeInt(mTag);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final View target = root.findViewById(mViewId);
+ if (target == null) return;
+
+ target.setTagInternal(mKey, mTag);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_INT_TAG_TAG;
+ }
+ }
+
/**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
@@ -2143,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);
}
@@ -2156,7 +2235,7 @@
*
* @hide
*/
- protected RemoteViews(ApplicationInfo application, int layoutId) {
+ protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
mApplication = application;
mLayoutId = layoutId;
mBitmapCache = new BitmapCache();
@@ -2182,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;
@@ -2203,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()) {
@@ -2262,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 {
@@ -2271,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) {
@@ -2326,6 +2407,8 @@
return new OverrideTextColorsAction(parcel);
case SET_RIPPLE_DRAWABLE_COLOR_TAG:
return new SetRippleDrawableColor(parcel);
+ case SET_INT_TAG_TAG:
+ return new SetIntTagAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -2360,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;
}
/**
@@ -3243,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;
@@ -3324,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);
@@ -3420,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
@@ -3603,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);
@@ -3616,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/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6cb0eaa..4caf288 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -31,6 +31,7 @@
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
+import android.text.util.Linkify;
import android.util.Log;
import android.view.ActionMode;
import android.view.textclassifier.SelectionEvent;
@@ -687,17 +688,6 @@
mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
}
- @TextClassifier.WidgetType
- private static String getWidetType(TextView textView) {
- if (textView.isTextEditable()) {
- return TextClassifier.WIDGET_TYPE_EDITTEXT;
- }
- if (textView.isTextSelectable()) {
- return TextClassifier.WIDGET_TYPE_TEXTVIEW;
- }
- return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
- }
-
public void logSelectionStarted(
TextClassifier classificationSession,
CharSequence text, int index,
@@ -1045,7 +1035,12 @@
trimText();
final TextClassification classification;
- if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+ if (Linkify.containsUnsupportedCharacters(mText)) {
+ // Do not show smart actions for text containing unsupported characters.
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ classification = TextClassification.EMPTY;
+ } else if (mContext.getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.P) {
final TextClassification.Request request =
new TextClassification.Request.Builder(
mTrimmedText, mRelativeStart, mRelativeEnd)
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index d55c09f..79dc670 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1419,27 +1419,10 @@
return Switch.class.getName();
}
+ /** @hide */
@Override
- public void onProvideStructure(ViewStructure structure) {
- super.onProvideStructure(structure);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- }
-
- @Override
- public void onProvideAutofillStructure(ViewStructure structure, int flags) {
- super.onProvideAutofillStructure(structure, flags);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- }
-
- @Override
- public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
- final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- return notifyManager;
- }
-
- // NOTE: currently there is no difference for any type, so it doesn't take flags
- private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) {
+ protected void onProvideStructure(@NonNull ViewStructure structure,
+ @ViewStructureType int viewFor, int flags) {
CharSequence switchText = isChecked() ? mTextOn : mTextOff;
if (!TextUtils.isEmpty(switchText)) {
CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35be766..2a42232 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.IntelligenceManager;
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
@@ -6254,8 +6309,13 @@
return mHint;
}
- @UnsupportedAppUsage
- boolean isSingleLine() {
+ /**
+ * Returns if the text is constrained to a single horizontally scrolling line ignoring new
+ * line characters instead of letting it wrap onto multiple lines.
+ *
+ * @attr ref android.R.styleable#TextView_singleLine
+ */
+ public boolean isSingleLine() {
return mSingleLine;
}
@@ -10130,7 +10190,7 @@
}
/**
- * Notify managers (such as {@link AutofillManager} and {@link IntelligenceManager}) that are
+ * Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are
* interested on text changes.
*/
private void notifyListeningManagersAfterTextChanged() {
@@ -10150,10 +10210,10 @@
// ContentCapture
if (isImportantForContentCapture() && isTextEditable()) {
- final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
- if (im != null && im.isContentCaptureEnabled()) {
+ final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+ if (cm != null && cm.isContentCaptureEnabled()) {
// TODO(b/111276913): pass flags when edited by user / add CTS test
- im.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
+ cm.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
}
}
}
@@ -10942,6 +11002,9 @@
if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
if (mLayout == null) {
+ if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
+ Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
+ }
assumeLayout();
}
Layout layout = mLayout;
@@ -12265,13 +12328,13 @@
}
/**
- * Returns the current {@link TextDirectionHeuristic}.
- *
- * @return the current {@link TextDirectionHeuristic}.
- * @hide
+ * Returns resolved {@link TextDirectionHeuristic} that will be used for text layout.
+ * The {@link TextDirectionHeuristic} that is used by TextView is only available after
+ * {@link #getTextDirection()} and {@link #getLayoutDirection()} is resolved. Therefore the
+ * return value may not be the same as the one TextView uses if the View's layout direction is
+ * not resolved or detached from parent root view.
*/
- @UnsupportedAppUsage
- protected TextDirectionHeuristic getTextDirectionHeuristic() {
+ public TextDirectionHeuristic getTextDirectionHeuristic() {
if (hasPasswordTransformationMethod()) {
// passwords fields should be LTR
return TextDirectionHeuristics.LTR;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e2b8f7d..5d08a25 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -309,9 +309,6 @@
} else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
- } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- prefer = RECOMMEND_INSTALL_EXTERNAL;
- checkBoth = false;
} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 34e8ed4..ff34036 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -19,7 +19,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
+import android.os.Process;
import android.os.SystemClock;
+import android.os.ThreadLocalWorkSource;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
@@ -52,17 +54,23 @@
public static final boolean ENABLED_DEFAULT = false;
public static final boolean DETAILED_TRACKING_DEFAULT = true;
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
+ public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
+
+ private static class OverflowBinder extends Binder {}
private static final String TAG = "BinderCallsStats";
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";
+ private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
+ private static final int OVERFLOW_TRANSACTION_CODE = -1;
// Whether to collect all the data: cpu + exceptions + reply/request sizes.
private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
// Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
// of 100 requests.
private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
+ private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT;
@GuardedBy("mLock")
private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
@GuardedBy("mLock")
@@ -71,8 +79,11 @@
private final Object mLock = new Object();
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 {
@@ -86,7 +97,11 @@
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.close();
+ }
mDeviceState = deviceState;
+ mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
}
@Override
@@ -148,8 +163,7 @@
return;
}
- final boolean isWorkSourceSet = workSourceUid >= 0;
- final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid);
+ final UidEntry uidEntry = getUidEntry(workSourceUid);
uidEntry.callCount++;
if (recordCall) {
@@ -158,7 +172,13 @@
final CallStat callStat = uidEntry.getOrCreate(
callingUid, s.binderClass, s.transactionCode,
- mDeviceState.isScreenInteractive());
+ mDeviceState.isScreenInteractive(),
+ mCallStatsCount >= mMaxBinderCallStatsCount);
+ final boolean isNewCallStat = callStat.callCount == 0;
+ if (isNewCallStat) {
+ mCallStatsCount++;
+ }
+
callStat.callCount++;
callStat.recordedCallCount++;
callStat.cpuTimeMicros += duration;
@@ -304,9 +324,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,6 +369,8 @@
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<>();
@@ -421,7 +464,7 @@
}
protected int getWorkSourceUid() {
- return Binder.getCallingWorkSourceUid();
+ return ThreadLocalWorkSource.getUid();
}
protected long getElapsedRealtimeMicro() {
@@ -444,6 +487,28 @@
}
}
+ public void setAddDebugEntries(boolean addDebugEntries) {
+ mAddDebugEntries = addDebugEntries;
+ }
+
+ /**
+ * Sets the maximum number of items to track.
+ */
+ public void setMaxBinderCallStats(int maxKeys) {
+ if (maxKeys <= 0) {
+ Slog.w(TAG, "Ignored invalid max value (value must be positive): "
+ + maxKeys);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (maxKeys != mMaxBinderCallStatsCount) {
+ mMaxBinderCallStatsCount = maxKeys;
+ reset();
+ }
+ }
+ }
+
public void setSamplingInterval(int samplingInterval) {
if (samplingInterval <= 0) {
Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
@@ -461,9 +526,13 @@
public void reset() {
synchronized (mLock) {
+ mCallStatsCount = 0;
mUidEntries.clear();
mExceptionCounts.clear();
mStartTime = System.currentTimeMillis();
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.reset();
+ }
}
}
@@ -595,10 +664,21 @@
}
CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
- int transactionCode, boolean screenInteractive) {
+ int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) {
CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
- // Only create CallStat if it's a new entry, otherwise update existing instance
+ // 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);
+ if (mapCallStat != null) {
+ return mapCallStat;
+ }
+
+ binderClass = OVERFLOW_BINDER;
+ transactionCode = OVERFLOW_TRANSACTION_CODE;
+ }
+
mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
screenInteractive);
CallStatKey key = new CallStatKey();
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/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
index 22435ae..b3aec0c 100644
--- a/core/java/com/android/internal/os/KernelCpuProcStringReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -59,6 +60,7 @@
private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state";
private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time";
private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time";
+ private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat";
private static final KernelCpuProcStringReader FREQ_TIME_READER =
new KernelCpuProcStringReader(PROC_UID_FREQ_TIME);
@@ -66,19 +68,25 @@
new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME);
private static final KernelCpuProcStringReader CLUSTER_TIME_READER =
new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME);
+ private static final KernelCpuProcStringReader USER_SYS_TIME_READER =
+ new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME);
- public static KernelCpuProcStringReader getFreqTimeReaderInstance() {
+ static KernelCpuProcStringReader getFreqTimeReaderInstance() {
return FREQ_TIME_READER;
}
- public static KernelCpuProcStringReader getActiveTimeReaderInstance() {
+ static KernelCpuProcStringReader getActiveTimeReaderInstance() {
return ACTIVE_TIME_READER;
}
- public static KernelCpuProcStringReader getClusterTimeReaderInstance() {
+ static KernelCpuProcStringReader getClusterTimeReaderInstance() {
return CLUSTER_TIME_READER;
}
+ static KernelCpuProcStringReader getUserSysTimeReaderInstance() {
+ return USER_SYS_TIME_READER;
+ }
+
private int mErrors = 0;
private final Path mFile;
private char[] mBuf;
@@ -164,12 +172,12 @@
// ReentrantReadWriteLock allows lock downgrading.
mReadLock.lock();
return new ProcFileIterator(total);
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | NoSuchFileException e) {
mErrors++;
Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile);
} catch (IOException e) {
mErrors++;
- Slog.e(TAG, "Error reading: " + mFile, e);
+ Slog.e(TAG, "Error reading " + mFile, e);
} finally {
StrictMode.setThreadPolicyMask(oldMask);
mWriteLock.unlock();
@@ -193,6 +201,11 @@
mSize = size;
}
+ /** @return Whether there are more lines in the iterator. */
+ public boolean hasNextLine() {
+ return mPos < mSize;
+ }
+
/**
* Fetches the next line. Note that all subsequent return values share the same char[]
* under the hood.
@@ -214,44 +227,6 @@
return CharBuffer.wrap(mBuf, start, i - start);
}
- /**
- * Fetches the next line, converts all numbers into long, and puts into the given long[].
- * To avoid GC, caller should try to use the same array for all calls. All non-numeric
- * chars are treated as delimiters. All numbers are non-negative.
- *
- * @param array An array to store the parsed numbers.
- * @return The number of elements written to the given array. -1 if there is no more line.
- */
- public int nextLineAsArray(long[] array) {
- CharBuffer buf = nextLine();
- if (buf == null) {
- return -1;
- }
- int count = 0;
- long num = -1;
- char c;
-
- while (buf.remaining() > 0 && count < array.length) {
- c = buf.get();
- if (num < 0) {
- if (isNumber(c)) {
- num = c - '0';
- }
- } else {
- if (isNumber(c)) {
- num = num * 10 + c - '0';
- } else {
- array[count++] = num;
- num = -1;
- }
- }
- }
- if (num >= 0) {
- array[count++] = num;
- }
- return count;
- }
-
/** Total size of the proc file in chars. */
public int size() {
return mSize;
@@ -262,8 +237,63 @@
mReadLock.unlock();
}
- private boolean isNumber(char c) {
- return c >= '0' && c <= '9';
+
+ }
+
+ /**
+ * Converts all numbers in the CharBuffer into longs, and puts into the given long[].
+ *
+ * Space and colon are treated as delimiters. All other chars are not allowed. All numbers
+ * are non-negative. To avoid GC, caller should try to use the same array for all calls.
+ *
+ * This method also resets the given buffer to the original position before return so that
+ * it can be read again.
+ *
+ * @param buf The char buffer to be converted.
+ * @param array An array to store the parsed numbers.
+ * @return The number of elements written to the given array. -1 if buf is null, -2 if buf
+ * contains invalid char, -3 if any number overflows.
+ */
+ public static int asLongs(CharBuffer buf, long[] array) {
+ if (buf == null) {
+ return -1;
}
+ final int initialPos = buf.position();
+ int count = 0;
+ long num = -1;
+ char c;
+
+ while (buf.remaining() > 0 && count < array.length) {
+ c = buf.get();
+ if (!(isNumber(c) || c == ' ' || c == ':')) {
+ buf.position(initialPos);
+ return -2;
+ }
+ if (num < 0) {
+ if (isNumber(c)) {
+ num = c - '0';
+ }
+ } else {
+ if (isNumber(c)) {
+ num = num * 10 + c - '0';
+ if (num < 0) {
+ buf.position(initialPos);
+ return -3;
+ }
+ } else {
+ array[count++] = num;
+ num = -1;
+ }
+ }
+ }
+ if (num >= 0) {
+ array[count++] = num;
+ }
+ buf.position(initialPos);
+ return count;
+ }
+
+ private static boolean isNumber(char c) {
+ return c >= '0' && c <= '9';
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3861739..2742b7c 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -103,10 +103,9 @@
/**
* Default predicate for what UIDs to check for when getting processes. This filters to only
- * select system UIDs (1000-1999)
+ * select UID 1000 (the {@code system} user)
*/
- private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
- uid -> 1000 <= uid && uid < 2000;
+ private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000;
/**
* Value returned when there was an error getting an integer ID value (e.g. PID, UID)
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
new file mode 100644
index 0000000..7021b57
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Reads per-UID CPU time proc files. Concrete implementations are all nested inside.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} or {@link #readAbsolute} call
+ * within {@link #mMinTimeBetweenRead}. The throttler can be enable / disabled via a param in
+ * the constructor.
+ *
+ * This class and its subclasses are NOT thread-safe and NOT designed to be accessed by more than
+ * one caller since each caller has its own view of delta.
+ *
+ * @param <T> The type of CPU time for the callback.
+ */
+public abstract class KernelCpuUidTimeReader<T> {
+ protected static final boolean DEBUG = false;
+ private static final long DEFAULT_MIN_TIME_BETWEEN_READ = 1000L; // In milliseconds
+
+ final String mTag = this.getClass().getSimpleName();
+ final SparseArray<T> mLastTimes = new SparseArray<>();
+ final KernelCpuProcStringReader mReader;
+ final boolean mThrottle;
+ private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
+ private long mLastReadTimeMs = 0;
+
+ /**
+ * Callback interface for processing each line of the proc file.
+ *
+ * @param <T> The type of CPU time for the callback function.
+ */
+ public interface Callback<T> {
+ /**
+ * @param uid UID of the app
+ * @param time Time spent. The exact data structure depends on subclass implementation.
+ */
+ void onUidCpuTime(int uid, T time);
+ }
+
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ mReader = reader;
+ mThrottle = throttle;
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with a delta of time for each UID.
+ *
+ * @param cb The callback to invoke for each line of the proc file. If null,the data is
+ * consumed and subsequent calls to readDelta will provide a fresh delta.
+ */
+ public void readDelta(@Nullable Callback<T> cb) {
+ if (!mThrottle) {
+ readDeltaImpl(cb);
+ return;
+ }
+ final long currTimeMs = SystemClock.elapsedRealtime();
+ if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (DEBUG) {
+ Slog.d(mTag, "Throttle readDelta");
+ }
+ return;
+ }
+ readDeltaImpl(cb);
+ mLastReadTimeMs = currTimeMs;
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with cumulative time for each UID.
+ *
+ * @param cb The callback to invoke for each line of the proc file. It cannot be null.
+ */
+ public void readAbsolute(Callback<T> cb) {
+ if (!mThrottle) {
+ readAbsoluteImpl(cb);
+ return;
+ }
+ final long currTimeMs = SystemClock.elapsedRealtime();
+ if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (DEBUG) {
+ Slog.d(mTag, "Throttle readAbsolute");
+ }
+ return;
+ }
+ readAbsoluteImpl(cb);
+ mLastReadTimeMs = currTimeMs;
+ }
+
+ abstract void readDeltaImpl(@Nullable Callback<T> cb);
+
+ abstract void readAbsoluteImpl(Callback<T> callback);
+
+ /**
+ * Removes the UID from internal accounting data. This method, overridden in
+ * {@link KernelCpuUidUserSysTimeReader}, also removes the UID from the kernel module.
+ *
+ * @param uid The UID to remove.
+ * @see KernelCpuUidUserSysTimeReader#removeUid(int)
+ */
+ public void removeUid(int uid) {
+ mLastTimes.delete(uid);
+ }
+
+ /**
+ * Removes UIDs in a given range from internal accounting data. This method, overridden in
+ * {@link KernelCpuUidUserSysTimeReader}, also removes the UIDs from the kernel module.
+ *
+ * @param startUid the first uid to remove.
+ * @param endUid the last uid to remove.
+ * @see KernelCpuUidUserSysTimeReader#removeUidsInRange(int, int)
+ */
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.e(mTag, "start UID " + startUid + " > end UID " + endUid);
+ return;
+ }
+ mLastTimes.put(startUid, null);
+ mLastTimes.put(endUid, null);
+ final int firstIndex = mLastTimes.indexOfKey(startUid);
+ final int lastIndex = mLastTimes.indexOfKey(endUid);
+ mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ /**
+ * Set the minimum time in milliseconds between reads. If throttle is not enabled, this method
+ * has no effect.
+ *
+ * @param minTimeBetweenRead The minimum time in milliseconds.
+ */
+ public void setThrottle(long minTimeBetweenRead) {
+ if (mThrottle && minTimeBetweenRead >= 0) {
+ mMinTimeBetweenRead = minTimeBetweenRead;
+ }
+ }
+
+ /**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+ public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
+
+ // [uid, user_time, system_time, (maybe) power_in_milli-amp-micro_seconds]
+ private final long[] mBuffer = new long[4];
+ // A reusable array to hold [user_time, system_time] for the callback.
+ private final long[] mUsrSysTime = new long[2];
+
+ public KernelCpuUidUserSysTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (iter == null) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) < 3) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[2];
+ mLastTimes.put(uid, lastTimes);
+ }
+ final long currUsrTimeUs = mBuffer[1];
+ final long currSysTimeUs = mBuffer[2];
+ mUsrSysTime[0] = currUsrTimeUs - lastTimes[0];
+ mUsrSysTime[1] = currSysTimeUs - lastTimes[1];
+
+ if (mUsrSysTime[0] < 0 || mUsrSysTime[1] < 0) {
+ Slog.e(mTag, "Negative user/sys time delta for UID=" + uid
+ + "\nPrev times: u=" + lastTimes[0] + " s=" + lastTimes[1]
+ + " Curr times: u=" + currUsrTimeUs + " s=" + currSysTimeUs);
+ } else if (mUsrSysTime[0] > 0 || mUsrSysTime[1] > 0) {
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mUsrSysTime);
+ }
+ }
+ lastTimes[0] = currUsrTimeUs;
+ lastTimes[1] = currSysTimeUs;
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (iter == null) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) < 3) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ mUsrSysTime[0] = mBuffer[1]; // User time in microseconds
+ mUsrSysTime[1] = mBuffer[2]; // System time in microseconds
+ cb.onUidCpuTime((int) mBuffer[0], mUsrSysTime);
+ }
+ }
+ }
+
+ @Override
+ public void removeUid(int uid) {
+ super.removeUid(uid);
+ removeUidsFromKernelModule(uid, uid);
+ }
+
+ @Override
+ public void removeUidsInRange(int startUid, int endUid) {
+ super.removeUidsInRange(startUid, endUid);
+ removeUidsFromKernelModule(startUid, endUid);
+ }
+
+ /**
+ * Removes UIDs in a given range from the kernel module and internal accounting data. Only
+ * {@link BatteryStatsImpl} and its child processes should call this, as the change on
+ * Kernel is
+ * visible system wide.
+ *
+ * @param startUid the first uid to remove
+ * @param endUid the last uid to remove
+ */
+ private void removeUidsFromKernelModule(int startUid, int endUid) {
+ Slog.d(mTag, "Removing uids " + startUid + "-" + endUid);
+ final int oldMask = StrictMode.allowThreadDiskWritesMask();
+ try (FileWriter writer = new FileWriter(REMOVE_UID_PROC_FILE)) {
+ writer.write(startUid + "-" + endUid);
+ writer.flush();
+ } catch (IOException e) {
+ Slog.e(mTag, "failed to remove uids " + startUid + " - " + endUid
+ + " from uid_cputime module", e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+ }
+
+ /**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+ public static class KernelCpuUidFreqTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+ // We check the existence of proc file a few times (just in case it is not ready yet when we
+ // start reading) and if it is not available, we simply ignore further read requests.
+ private static final int MAX_ERROR_COUNT = 5;
+
+ private final Path mProcFilePath;
+ private long[] mBuffer;
+ private long[] mCurTimes;
+ private long[] mDeltaTimes;
+ private long[] mCpuFreqs;
+
+ private int mFreqCount = 0;
+ private int mErrors = 0;
+ private boolean mPerClusterTimesAvailable;
+ private boolean mAllUidTimesAvailable = true;
+
+ public KernelCpuUidFreqTimeReader(boolean throttle) {
+ this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
+ throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
+ boolean throttle) {
+ super(reader, throttle);
+ mProcFilePath = Paths.get(procFile);
+ }
+
+ /**
+ * @return Whether per-cluster times are available.
+ */
+ public boolean perClusterTimesAvailable() {
+ return mPerClusterTimesAvailable;
+ }
+
+ /**
+ * @return Whether all-UID times are available.
+ */
+ public boolean allUidTimesAvailable() {
+ return mAllUidTimesAvailable;
+ }
+
+ /**
+ * @return A map of all UIDs to their CPU time-in-state array in milliseconds.
+ */
+ public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
+ return mLastTimes;
+ }
+
+ /**
+ * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
+ * to determine if per-cluster times are available.
+ *
+ * @param powerProfile The PowerProfile to compare against.
+ * @return A long[] of CPU frequencies in Hz.
+ */
+ public long[] readFreqs(@NonNull PowerProfile powerProfile) {
+ checkNotNull(powerProfile);
+ if (mCpuFreqs != null) {
+ // No need to read cpu freqs more than once.
+ return mCpuFreqs;
+ }
+ if (!mAllUidTimesAvailable) {
+ return null;
+ }
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+ if (readFreqs(reader.readLine()) == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ if (++mErrors >= MAX_ERROR_COUNT) {
+ mAllUidTimesAvailable = false;
+ }
+ Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ // Check if the freqs in the proc file correspond to per-cluster freqs.
+ final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
+ final int numClusters = powerProfile.getNumCpuClusters();
+ if (numClusterFreqs.size() == numClusters) {
+ mPerClusterTimesAvailable = true;
+ for (int i = 0; i < numClusters; ++i) {
+ if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
+ mPerClusterTimesAvailable = false;
+ break;
+ }
+ }
+ } else {
+ mPerClusterTimesAvailable = false;
+ }
+ Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
+ return mCpuFreqs;
+ }
+
+ private long[] readFreqs(String line) {
+ if (line == null) {
+ return null;
+ }
+ final String[] lineArray = line.split(" ");
+ if (lineArray.length <= 1) {
+ Slog.wtf(mTag, "Malformed freq line: " + line);
+ return null;
+ }
+ mFreqCount = lineArray.length - 1;
+ mCpuFreqs = new long[mFreqCount];
+ mCurTimes = new long[mFreqCount];
+ mDeltaTimes = new long[mFreqCount];
+ mBuffer = new long[mFreqCount + 1];
+ for (int i = 0; i < mFreqCount; ++i) {
+ mCpuFreqs[i] = Long.parseLong(lineArray[i + 1], 10);
+ }
+ return mCpuFreqs;
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mFreqCount];
+ mLastTimes.put(uid, lastTimes);
+ }
+ copyToCurTimes();
+ boolean notify = false;
+ boolean valid = true;
+ for (int i = 0; i < mFreqCount; i++) {
+ // Unit is 10ms.
+ mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+ if (mDeltaTimes[i] < 0) {
+ Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ valid = false;
+ }
+ notify |= mDeltaTimes[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTimes);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ copyToCurTimes();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+ }
+ }
+ }
+
+ private void copyToCurTimes() {
+ for (int i = 0; i < mFreqCount; i++) {
+ mCurTimes[i] = mBuffer[i + 1] * 10;
+ }
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mCpuFreqs != null) {
+ return true;
+ }
+ return readFreqs(line.toString()) != null;
+ }
+
+ /**
+ * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
+ * read from the proc file.
+ *
+ * We need to assume that freqs in each cluster are strictly increasing.
+ * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
+ * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
+ *
+ * @return an IntArray filled with no. of freqs in each cluster.
+ */
+ private IntArray extractClusterInfoFromProcFileFreqs() {
+ final IntArray numClusterFreqs = new IntArray();
+ int freqsFound = 0;
+ for (int i = 0; i < mFreqCount; ++i) {
+ freqsFound++;
+ if (i + 1 == mFreqCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
+ numClusterFreqs.add(freqsFound);
+ freqsFound = 0;
+ }
+ }
+ return numClusterFreqs;
+ }
+ }
+
+ /**
+ * Reads /proc/uid_concurrent_active_time and reports CPU active time to BatteryStats to
+ * compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+ *
+ * /proc/uid_concurrent_active_time has the following format:
+ * cpus: n
+ * uid0: time0a, time0b, ..., time0n,
+ * uid1: time1a, time1b, ..., time1n,
+ * uid2: time2a, time2b, ..., time2n,
+ * ...
+ * where n is the total number of cpus (num_possible_cpus)
+ * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
+ *
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+ public static class KernelCpuUidActiveTimeReader extends KernelCpuUidTimeReader<Long> {
+ private int mCores = 0;
+ private long[] mBuffer;
+
+ public KernelCpuUidActiveTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<Long> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ int uid = (int) mBuffer[0];
+ long cpuActiveTime = sumActiveTime(mBuffer);
+ if (cpuActiveTime > 0) {
+ long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+ if (delta > 0) {
+ mLastTimes.put(uid, cpuActiveTime);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, delta);
+ }
+ } else if (delta < 0) {
+ Slog.e(mTag, "Negative delta from active time proc: " + delta);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<Long> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ long cpuActiveTime = sumActiveTime(mBuffer);
+ if (cpuActiveTime > 0) {
+ cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+ }
+ }
+ }
+ }
+
+ private static long sumActiveTime(long[] times) {
+ // UID is stored at times[0].
+ double sum = 0;
+ for (int i = 1; i < times.length; i++) {
+ sum += (double) times[i] * 10 / i; // Unit is 10ms.
+ }
+ return (long) sum;
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mCores > 0) {
+ return true;
+ }
+
+ String str = line.toString();
+ if (!str.startsWith("cpus:")) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+ return false;
+ }
+ int cores = Integer.parseInt(str.substring(5).trim(), 10);
+ if (cores <= 0) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+ return false;
+ }
+ mCores = cores;
+ mBuffer = new long[mCores + 1]; // UID is stored at mBuffer[0].
+ return true;
+ }
+ }
+
+
+ /**
+ * Reads /proc/uid_concurrent_policy_time and reports CPU cluster times to BatteryStats to
+ * compute cluster power. See {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+ *
+ * /proc/uid_concurrent_policy_time has the following format:
+ * policyX: x policyY: y policyZ: z...
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n,
+ * ...
+ * The first line lists all policies (i.e. clusters) followed by # cores in each policy.
+ * Each uid is followed by x time entries corresponding to the time it spent on clusterX
+ * running concurrently with 0, 1, 2, ..., x - 1 other processes, then followed by y, z, ...
+ * time entries.
+ *
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+ public static class KernelCpuUidClusterTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private int mNumClusters;
+ private int mNumCores;
+ private int[] mCoresOnClusters; // # cores on each cluster.
+ private long[] mBuffer; // To store data returned from ProcFileIterator.
+ private long[] mCurTime;
+ private long[] mDeltaTime;
+
+ public KernelCpuUidClusterTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mNumClusters];
+ mLastTimes.put(uid, lastTimes);
+ }
+ sumClusterTime();
+ boolean valid = true;
+ boolean notify = false;
+ for (int i = 0; i < mNumClusters; i++) {
+ mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+ if (mDeltaTime[i] < 0) {
+ Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ valid = false;
+ }
+ notify |= mDeltaTime[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTime);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ sumClusterTime();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+ }
+ }
+ }
+
+ private void sumClusterTime() {
+ // UID is stored at mBuffer[0].
+ int core = 1;
+ for (int i = 0; i < mNumClusters; i++) {
+ double sum = 0;
+ for (int j = 1; j <= mCoresOnClusters[i]; j++) {
+ sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+ }
+ mCurTime[i] = (long) sum;
+ }
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mNumClusters > 0) {
+ return true;
+ }
+ // Parse # cores in clusters.
+ String[] lineArray = line.toString().split(" ");
+ if (lineArray.length % 2 != 0) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+ return false;
+ }
+ int[] clusters = new int[lineArray.length / 2];
+ int cores = 0;
+ for (int i = 0; i < clusters.length; i++) {
+ if (!lineArray[i * 2].startsWith("policy")) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+ return false;
+ }
+ clusters[i] = Integer.parseInt(lineArray[i * 2 + 1], 10);
+ cores += clusters[i];
+ }
+ mNumClusters = clusters.length;
+ mNumCores = cores;
+ mCoresOnClusters = clusters;
+ mBuffer = new long[cores + 1];
+ mCurTime = new long[mNumClusters];
+ mDeltaTime = new long[mNumClusters];
+ return true;
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 2b661c2..01fd8ba 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -49,7 +49,9 @@
private final int mEntriesSizeCap;
private int mSamplingInterval;
private CachedDeviceState.Readonly mDeviceState;
+ private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
private long mStartTime = System.currentTimeMillis();
+ private boolean mAddDebugEntries = false;
public LooperStats(int samplingInterval, int entriesSizeCap) {
this.mSamplingInterval = samplingInterval;
@@ -57,7 +59,16 @@
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.close();
+ }
+
mDeviceState = deviceState;
+ mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
+ }
+
+ public void setAddDebugEntries(boolean addDebugEntries) {
+ mAddDebugEntries = addDebugEntries;
}
@Override
@@ -142,14 +153,33 @@
// Add the overflow and collision entries only if they have any data.
maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
+ // Debug entries added to help validate the data.
+ 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;
}
+ private ExportedEntry createDebugEntry(String variableName, long value) {
+ final Entry entry = new Entry("__DEBUG_" + variableName);
+ entry.messageCount = 1;
+ entry.recordedMessageCount = 1;
+ entry.totalLatencyMicro = value;
+ return new ExportedEntry(entry);
+ }
+
/** Returns a timestamp indicating when the statistics were last reset. */
public long getStartTimeMillis() {
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) {
@@ -170,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/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index b9d53c1..d78bfac 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -120,6 +120,8 @@
arg5 = null;
arg6 = null;
arg7 = null;
+ arg8 = null;
+ arg9 = null;
argi1 = 0;
argi2 = 0;
argi3 = 0;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 3b7ce0a..d8ee643 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -41,6 +41,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -90,6 +91,7 @@
import android.view.ViewRootImpl;
import android.view.ViewRootImpl.ActivityConfigCallback;
import android.view.Window;
+import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -1891,7 +1893,7 @@
return true;
}
// These are all the recognized media key codes in
- // KeyEvent.isMediaKey()
+ // KeyEvent.isMediaSessionKey()
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -1992,7 +1994,7 @@
return true;
}
// These are all the recognized media key codes in
- // KeyEvent.isMediaKey()
+ // KeyEvent.isMediaSessionKey()
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -3876,4 +3878,9 @@
mDecor.updateLogTag(params);
}
}
+
+ @Override
+ public WindowInsetsController getInsetsController() {
+ return mDecor.getWindowInsetsController();
+ }
}
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
new file mode 100644
index 0000000..100c6ee
--- /dev/null
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.policy;
+
+import com.android.internal.R;
+
+import android.content.res.Resources;
+
+/**
+ * Utility functions for screen decorations used by both window manager and System UI.
+ */
+public class ScreenDecorationsUtils {
+
+ /**
+ * Corner radius that should be used on windows in order to cover the display.
+ * These values are expressed in pixels because they should not respect display or font
+ * scaling, this means that we don't have to reload them on config changes.
+ */
+ public static float getWindowCornerRadius(Resources resources) {
+ // Radius that should be used in case top or bottom aren't defined.
+ float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius);
+
+ float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top);
+ if (topRadius == 0) {
+ topRadius = defaultRadius;
+ }
+ float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom);
+ if (bottomRadius == 0) {
+ bottomRadius = defaultRadius;
+ }
+
+ // Always use the smallest radius to make sure the rounded corners will
+ // completely cover the display.
+ return Math.min(topRadius, bottomRadius);
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 604537f..53b56f2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,7 +18,7 @@
import android.content.ComponentName;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -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);
@@ -141,7 +150,7 @@
void showShutdownUi(boolean isReboot, String reason);
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
@@ -151,4 +160,6 @@
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+ // Used to request the "try again" button for authentications which requireConfirmation=true
+ void showBiometricTryAgain();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9a7094a..5118e5f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -16,11 +16,12 @@
package com.android.internal.statusbar;
+import android.app.Notification;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -40,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);
@@ -55,7 +57,7 @@
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
void clearNotificationEffects();
void onNotificationClick(String key, in NotificationVisibility nv);
- void onNotificationActionClick(String key, int actionIndex, in NotificationVisibility nv);
+ void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant);
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
void onClearAllNotifications(int userId);
@@ -66,9 +68,9 @@
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
void onNotificationDirectReplied(String key);
void onNotificationSmartRepliesAdded(in String key, in int replyCount);
- void onNotificationSmartReplySent(in String key, in int replyIndex);
+ 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();
@@ -91,7 +93,7 @@
void showPinningEscapeToast();
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
@@ -101,4 +103,6 @@
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+ // Used to request the "try again" button for authentications which requireConfirmation=true
+ void showBiometricTryAgain();
}
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/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 17d5a2e3..6158145 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -28,7 +28,7 @@
/**
* A utility class for handling unsigned integers and unsigned arithmetics, as well as syntactic
- * sugar methods for ByteBuffer. Useful for networking and packet manipulations.
+ * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations.
* {@hide}
*/
public final class BitUtils {
@@ -151,4 +151,11 @@
TextUtils.wrap(builder, "[", "]");
return builder.toString();
}
+
+ /**
+ * Converts long to byte array
+ */
+ public static byte[] toBytes(long l) {
+ return ByteBuffer.allocate(8).putLong(l).array();
+ }
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 083c0c9..151901b 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -190,6 +191,13 @@
}
/**
+ * Returns the size of the given map, or 0 if null
+ */
+ public static int size(@Nullable Map<?, ?> cur) {
+ return cur != null ? cur.size() : 0;
+ }
+
+ /**
* Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
*/
public static boolean isEmpty(@Nullable Collection<?> cur) {
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 5da587b..344d7ef 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -63,7 +63,7 @@
public static final int
convertValueToList(CharSequence value, String[] options, int defaultValue)
{
- if (null != value) {
+ if (!TextUtils.isEmpty(value)) {
for (int i = 0; i < options.length; i++) {
if (value.equals(options[i]))
return i;
@@ -79,8 +79,9 @@
{
boolean result = false;
- if (null == value)
+ if (TextUtils.isEmpty(value)) {
return defaultValue;
+ }
if (value.equals("1")
|| value.equals("true")
@@ -94,8 +95,9 @@
public static final int
convertValueToInt(CharSequence charSeq, int defaultValue)
{
- if (null == charSeq)
+ if (TextUtils.isEmpty(charSeq)) {
return defaultValue;
+ }
String nm = charSeq.toString();
@@ -138,7 +140,7 @@
}
public static int convertValueToUnsignedInt(String value, int defaultValue) {
- if (null == value) {
+ if (TextUtils.isEmpty(value)) {
return defaultValue;
}
@@ -1674,7 +1676,7 @@
public static boolean readBooleanAttribute(XmlPullParser in, String name,
boolean defaultValue) {
final String value = in.getAttributeValue(null, name);
- if (value == null) {
+ if (TextUtils.isEmpty(value)) {
return defaultValue;
} else {
return Boolean.parseBoolean(value);
@@ -1711,7 +1713,7 @@
public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
final String value = in.getAttributeValue(null, name);
- if (value != null) {
+ if (!TextUtils.isEmpty(value)) {
return Base64.decode(value, Base64.DEFAULT);
} else {
return null;
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaConsumer.java
similarity index 72%
rename from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
rename to core/java/com/android/internal/util/function/NonaConsumer.java
index 27d25b8..3e7ce2b 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaConsumer.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Consumer}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface NonaConsumer<A, B, C, D, E, F, G, H, I> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaFunction.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaFunction.java
index 27d25b8..560b4f1 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaFunction.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Function}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface NonaFunction<A, B, C, D, E, F, G, H, I, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaPredicate.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaPredicate.java
index 27d25b8..c1e6f37 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaPredicate.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Predicate}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface NonaPredicate<A, B, C, D, E, F, G, H, I> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctConsumer.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctConsumer.java
index 27d25b8..83ee305 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctConsumer.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Consumer}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface OctConsumer<A, B, C, D, E, F, G, H> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctFunction.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctFunction.java
index 27d25b8..cb16624 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctFunction.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Function}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface OctFunction<A, B, C, D, E, F, G, H, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctPredicate.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctPredicate.java
index 27d25b8..7f36d6a 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctPredicate.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Predicate}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface OctPredicate<A, B, C, D, E, F, G, H> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h);
}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
index 4ffe441..d74e715 100755
--- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -22,6 +22,10 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintConsumer;
@@ -39,61 +43,62 @@
*
* @hide
*/
-abstract class OmniFunction<A, B, C, D, E, F, G, R> implements
+abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>,
HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>,
+ OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>,
PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>,
- HeptConsumer<A, B, C, D, E, F, G>,
- PooledPredicate<A>, BiPredicate<A, B>,
+ HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>,
+ NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>,
PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>,
PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
- abstract R invoke(A a, B b, C c, D d, E e, F f, G g);
+ abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i);
@Override
public R apply(A o, B o2) {
- return invoke(o, o2, null, null, null, null, null);
+ return invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public R apply(A o) {
- return invoke(o, null, null, null, null, null, null);
+ return invoke(o, null, null, null, null, null, null, null, null);
}
- public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen(
+ public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen(
Function<? super R, ? extends V> after);
- public abstract OmniFunction<A, B, C, D, E, F, G, R> negate();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate();
@Override
public void accept(A o, B o2) {
- invoke(o, o2, null, null, null, null, null);
+ invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public void accept(A o) {
- invoke(o, null, null, null, null, null, null);
+ invoke(o, null, null, null, null, null, null, null, null);
}
@Override
public void run() {
- invoke(null, null, null, null, null, null, null);
+ invoke(null, null, null, null, null, null, null, null, null);
}
@Override
public R get() {
- return invoke(null, null, null, null, null, null, null);
+ return invoke(null, null, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o, B o2) {
- return (Boolean) invoke(o, o2, null, null, null, null, null);
+ return (Boolean) invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o) {
- return (Boolean) invoke(o, null, null, null, null, null, null);
+ return (Boolean) invoke(o, null, null, null, null, null, null, null, null);
}
@Override
@@ -108,52 +113,72 @@
@Override
public R apply(A a, B b, C c) {
- return invoke(a, b, c, null, null, null, null);
+ return invoke(a, b, c, null, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c) {
- invoke(a, b, c, null, null, null, null);
+ invoke(a, b, c, null, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d) {
- return invoke(a, b, c, d, null, null, null);
+ return invoke(a, b, c, d, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e) {
- return invoke(a, b, c, d, e, null, null);
+ return invoke(a, b, c, d, e, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f) {
- return invoke(a, b, c, d, e, f, null);
+ return invoke(a, b, c, d, e, f, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f, G g) {
- return invoke(a, b, c, d, e, f, g);
+ return invoke(a, b, c, d, e, f, g, null, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h) {
+ return invoke(a, b, c, d, e, f, g, h, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ return invoke(a, b, c, d, e, f, g, h, i);
}
@Override
public void accept(A a, B b, C c, D d) {
- invoke(a, b, c, d, null, null, null);
+ invoke(a, b, c, d, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e) {
- invoke(a, b, c, d, e, null, null);
+ invoke(a, b, c, d, e, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f) {
- invoke(a, b, c, d, e, f, null);
+ invoke(a, b, c, d, e, f, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f, G g) {
- invoke(a, b, c, d, e, f, g);
+ invoke(a, b, c, d, e, f, g, null, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h) {
+ invoke(a, b, c, d, e, f, g, h, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ invoke(a, b, c, d, e, f, g, h, i);
}
@Override
@@ -167,5 +192,5 @@
}
@Override
- public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse();
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index af3c752..c00932e 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -25,6 +25,10 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintConsumer;
@@ -176,7 +180,8 @@
Consumer<? super A> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -192,7 +197,8 @@
Predicate<? super A> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -208,7 +214,8 @@
Function<? super A, ? extends R> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -238,7 +245,8 @@
A arg1) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -257,7 +265,8 @@
BiConsumer<? super A, ? super B> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -274,7 +283,8 @@
BiPredicate<? super A, ? super B> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -291,7 +301,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -308,7 +319,8 @@
BiConsumer<? super A, ? super B> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -325,7 +337,8 @@
BiPredicate<? super A, ? super B> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -342,7 +355,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -359,7 +373,8 @@
BiConsumer<? super A, ? super B> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -376,7 +391,8 @@
BiPredicate<? super A, ? super B> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -393,7 +409,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -424,7 +441,8 @@
A arg1, B arg2) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -444,7 +462,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -462,7 +481,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -480,7 +500,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -498,7 +519,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -516,7 +538,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -534,7 +557,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -552,7 +576,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -570,7 +595,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -602,7 +628,8 @@
A arg1, B arg2, C arg3) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -623,7 +650,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -642,7 +670,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -661,7 +690,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -680,7 +710,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -699,7 +730,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -718,7 +750,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -737,7 +770,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -756,7 +790,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -775,7 +810,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -794,7 +830,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -827,7 +864,8 @@
A arg1, B arg2, C arg3, D arg4) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -849,7 +887,8 @@
QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function,
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
}
/**
@@ -869,7 +908,8 @@
QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R>
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
}
/**
@@ -904,7 +944,8 @@
A arg1, B arg2, C arg3, D arg4, E arg5) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -927,7 +968,8 @@
HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
}
/**
@@ -948,7 +990,8 @@
HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
}
/**
@@ -984,7 +1027,8 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1008,7 +1052,8 @@
HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
}
/**
@@ -1031,7 +1076,8 @@
? super G, ? extends R> function,
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
}
/**
@@ -1068,7 +1114,195 @@
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+ */
+ static <A, B, C, D, E, F, G, H> PooledRunnable obtainRunnable(
+ OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+ ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+ H arg8) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+ */
+ static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier(
+ OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8) } when handled
+ */
+ static <A, B, C, D, E, F, G, H> Message obtainMessage(
+ OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+ ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+ H arg8) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+ */
+ static <A, B, C, D, E, F, G, H, I> PooledRunnable obtainRunnable(
+ NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+ E arg5, F arg6, G arg7, H arg8, I arg9) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+ */
+ static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier(
+ NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I> Message obtainMessage(
+ NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+ E arg5, F arg6, G arg7, H arg8, I arg9) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index eea1e5f..6be626a 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -30,6 +30,12 @@
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.HexPredicate;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.NonaPredicate;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.OctPredicate;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuadPredicate;
@@ -54,12 +60,12 @@
* @hide
*/
final class PooledLambdaImpl<R> extends OmniFunction<Object,
- Object, Object, Object, Object, Object, Object, R> {
+ Object, Object, Object, Object, Object, Object, Object, Object, R> {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "PooledLambdaImpl";
- private static final int MAX_ARGS = 7;
+ private static final int MAX_ARGS = 9;
private static final int MAX_POOL_SIZE = 50;
@@ -125,7 +131,7 @@
/**
* Bit schema:
- * AAAAAAABCDEEEEEEFFFFFF
+ * AAAAAAAAABCDEEEEEEFFFFFF
*
* Where:
* A - whether {@link #mArgs arg} at corresponding index was specified at
@@ -161,17 +167,19 @@
}
@Override
- R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+ R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7,
+ Object a8, Object a9) {
checkNotRecycled();
if (DEBUG) {
Log.i(LOG_TAG, this + ".invoke("
+ commaSeparateFirstN(
- new Object[] { a1, a2, a3, a4, a5, a6, a7 },
+ new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 },
LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
+ ")");
}
- final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3)
- && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7);
+ final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4)
+ && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8)
+ && fillInArg(a9);
int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
if (argCount != LambdaType.MASK_ARG_COUNT) {
for (int i = 0; i < argCount; i++) {
@@ -335,7 +343,7 @@
popArg(2), popArg(3), popArg(4), popArg(5));
}
}
- }
+ } break;
case 7: {
switch (returnType) {
@@ -356,7 +364,49 @@
popArg(5), popArg(6));
}
}
- }
+ } break;
+
+ case 8: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((OctConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((OctPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3),
+ popArg(4), popArg(5), popArg(6), popArg(7));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7));
+ }
+ }
+ } break;
+
+ case 9: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((NonaConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -419,8 +469,8 @@
* Internal non-typesafe factory method for {@link PooledLambdaImpl}
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
- int fNumArgs, int numPlaceholders, int fReturnType,
- Object a, Object b, Object c, Object d, Object e, Object f, Object g) {
+ int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
+ Object d, Object e, Object f, Object g, Object h, Object i) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -436,6 +486,8 @@
+ ", e = " + e
+ ", f = " + f
+ ", g = " + g
+ + ", h = " + h
+ + ", i = " + i
+ ")");
}
r.mFunc = func;
@@ -449,6 +501,8 @@
setIfInBounds(r.mArgs, 4, e);
setIfInBounds(r.mArgs, 5, f);
setIfInBounds(r.mArgs, 6, g);
+ setIfInBounds(r.mArgs, 7, h);
+ setIfInBounds(r.mArgs, 8, i);
return (E) r;
}
@@ -474,13 +528,14 @@
}
@Override
- public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() {
+ public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ R> negate() {
throw new UnsupportedOperationException();
}
@Override
- public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen(
- Function<? super R, ? extends V> after) {
+ public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ V> andThen(Function<? super R, ? extends V> after) {
throw new UnsupportedOperationException();
}
@@ -500,7 +555,8 @@
}
@Override
- public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() {
+ public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ R> recycleOnUse() {
if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
mFlags |= FLAG_RECYCLE_ON_USE;
return this;
@@ -584,6 +640,8 @@
case 5: return "Quint";
case 6: return "Hex";
case 7: return "Hept";
+ case 8: return "Oct";
+ case 9: return "Nona";
default: throw new IllegalArgumentException("" + argCount);
}
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 137ca7f..c8834a8 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -27,6 +27,8 @@
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.PointerIcon;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
@@ -53,6 +55,15 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) throws RemoteException {
+ }
+
+ @Override
public void moved(int newX, int newY) {
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/widget/ImageMessageConsumer.java
similarity index 61%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/widget/ImageMessageConsumer.java
index 27d25b8..01613dc 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/widget/ImageMessageConsumer.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.widget;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
+ * An interface for the class who will use the {@link ImageResolver} to resolve images.
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+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/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/widget/ImageResolver.java
similarity index 60%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/widget/ImageResolver.java
index 27d25b8..4588525 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/widget/ImageResolver.java
@@ -13,12 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.widget;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
+ * An interface for image resolvers that have custom implementations like cache mechanisms.
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+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/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7635a72..b7e656b 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -814,7 +814,14 @@
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.alwaysShow && child.getVisibility() != GONE) {
- measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ if (lp.maxHeight != -1) {
+ final int remainingHeight = heightSize - heightUsed;
+ measureChildWithMargins(child, widthSpec, widthPadding,
+ MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+ lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+ } else {
+ measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ }
heightUsed += child.getMeasuredHeight();
}
}
@@ -824,9 +831,17 @@
// And now the rest.
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.alwaysShow && child.getVisibility() != GONE) {
- measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ if (lp.maxHeight != -1) {
+ final int remainingHeight = heightSize - heightUsed;
+ measureChildWithMargins(child, widthSpec, widthPadding,
+ MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+ lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+ } else {
+ measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ }
heightUsed += child.getMeasuredHeight();
}
}
@@ -938,6 +953,7 @@
public boolean alwaysShow;
public boolean ignoreOffset;
public boolean hasNestedScrollIndicator;
+ public int maxHeight;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
@@ -953,6 +969,8 @@
hasNestedScrollIndicator = a.getBoolean(
R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator,
false);
+ maxHeight = a.getDimensionPixelSize(
+ R.styleable.ResolverDrawerLayout_LayoutParams_layout_maxHeight, -1);
a.recycle();
}
@@ -965,6 +983,7 @@
this.alwaysShow = source.alwaysShow;
this.ignoreOffset = source.ignoreOffset;
this.hasNestedScrollIndicator = source.hasNestedScrollIndicator;
+ this.maxHeight = source.maxHeight;
}
public LayoutParams(MarginLayoutParams source) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 15745e9..b97a9fa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.Environment;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.text.TextUtils;
@@ -930,6 +931,16 @@
XmlUtils.skipCurrentTag(parser);
}
}
+ // If the storage model feature flag is disabled, we need to fiddle
+ // around with permission definitions to return us to pre-Q behavior.
+ // STOPSHIP(b/112545973): remove once feature enabled by default
+ if (!StorageManager.hasIsolatedStorage()) {
+ if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
+ newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
+ newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
+ return;
+ }
+ }
if (!newPermissions.isEmpty()) {
mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index bdd5f83..31bb1d5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -34,7 +34,6 @@
],
cppflags: ["-Wno-conversion-null"],
- cpp_std: "c++17",
srcs: [
"AndroidRuntime.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index eada690..f9879cc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1056,12 +1056,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,8 +1456,8 @@
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
- REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioSystem),
+ REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioTrack),
REG_JNI(register_android_media_JetPlayer),
REG_JNI(register_android_media_MicrophoneInfo),
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index dcb7874..cfe742d 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -59,7 +59,7 @@
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);
+ paint->setTextEncoding(kGlyphID_SkTextEncoding);
SkScalar skWidth;
SkRect skBounds;
@@ -84,7 +84,7 @@
{
HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
SkPaint* paint = hbFontData->m_paint;
- paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
+ paint->setTextEncoding(kUTF32_SkTextEncoding);
if (unicode > 0x10ffff) {
unicode = 0xfffd;
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/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 68f5bef..ed6a84b 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -64,11 +64,10 @@
jint tileModeX, jint tileModeY) {
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
sk_sp<SkImage> image;
- sk_sp<SkColorFilter> colorFilter;
if (jbitmap) {
// Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
// we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
- image = android::bitmap::toBitmap(env, jbitmap).makeImage(&colorFilter);
+ image = android::bitmap::toBitmap(env, jbitmap).makeImage();
}
if (!image.get()) {
@@ -81,9 +80,6 @@
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
- if(colorFilter) {
- shader = shader->makeWithColorFilter(colorFilter);
- }
ThrowIAE_IfNull(env, shader.get());
return reinterpret_cast<jlong>(shader.release());
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index e02741f..719cf74 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -514,14 +514,14 @@
sp<ANativeWindow> anw;
if ((anw = getNativeWindow(env, surface)) == NULL) {
- jniThrowException(env, "java/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
"Could not retrieve native window from surface.");
return BAD_VALUE;
}
int32_t usage = 0;
status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
if(err != NO_ERROR) {
- jniThrowException(env, "java/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
"Error while querying surface usage bits");
OVERRIDE_SURFACE_ERROR(err);
return err;
@@ -542,7 +542,7 @@
status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
if(err != NO_ERROR) {
- jniThrowException(env, "java/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
"Error while disconnecting surface");
OVERRIDE_SURFACE_ERROR(err);
return err;
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 05f6556..e74aafe 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -40,6 +40,7 @@
jfieldID deviceWidth;
jfieldID deviceHeight;
jfieldID uniqueId;
+ jfieldID physicalPort;
jfieldID type;
} gDisplayViewportClassInfo;
@@ -54,6 +55,9 @@
status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewportObj,
DisplayViewport* viewport) {
+ static const jclass byteClass = FindClassOrDie(env, "java/lang/Byte");
+ static const jmethodID byteValue = env->GetMethodID(byteClass, "byteValue", "()B");
+
viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
@@ -65,6 +69,12 @@
viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
}
+ viewport->physicalPort = std::nullopt;
+ jobject physicalPort = env->GetObjectField(viewportObj, gDisplayViewportClassInfo.physicalPort);
+ if (physicalPort != nullptr) {
+ viewport->physicalPort = std::make_optional(env->CallByteMethod(physicalPort, byteValue));
+ }
+
viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
gDisplayViewportClassInfo.type));
@@ -112,6 +122,9 @@
gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
+ gDisplayViewportClassInfo.physicalPort = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "physicalPort", "Ljava/lang/Byte;");
+
gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "type", "I");
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 5887fa7..10005dd 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -22,6 +22,7 @@
#include <utils/threads.h>
#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
namespace android {
@@ -29,6 +30,7 @@
jfieldID ptr;
jfieldID name;
jfieldID dispatchingTimeoutNanos;
+ jfieldID token;
} gInputApplicationHandleClassInfo;
static Mutex gHandleMutex;
@@ -75,6 +77,15 @@
mInfo->dispatchingTimeout = env->GetLongField(obj,
gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+ jobject tokenObj = env->GetObjectField(obj,
+ gInputApplicationHandleClassInfo.token);
+ if (tokenObj) {
+ mInfo->token = ibinderForJavaObject(env, tokenObj);
+ env->DeleteLocalRef(tokenObj);
+ } else {
+ mInfo->token.clear();
+ }
+
env->DeleteLocalRef(obj);
return true;
}
@@ -153,6 +164,9 @@
clazz,
"dispatchingTimeoutNanos", "J");
+ GET_FIELD_ID(gInputApplicationHandleClassInfo.token, clazz,
+ "token", "Landroid/os/IBinder;");
+
return 0;
}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 6ecb5de..76920f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -21,19 +21,19 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
-#include <android_view_InputChannel.h>
#include <android/graphics/Region.h>
#include <ui/Region.h>
#include "android_hardware_input_InputWindowHandle.h"
#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
namespace android {
static struct {
jfieldID ptr;
jfieldID inputApplicationHandle;
- jfieldID inputChannel;
+ jfieldID token;
jfieldID name;
jfieldID layoutParamsFlags;
jfieldID layoutParamsType;
@@ -42,6 +42,7 @@
jfieldID frameTop;
jfieldID frameRight;
jfieldID frameBottom;
+ jfieldID surfaceInset;
jfieldID scaleFactor;
jfieldID touchableRegion;
jfieldID visible;
@@ -61,9 +62,7 @@
// --- NativeInputWindowHandle ---
-NativeInputWindowHandle::NativeInputWindowHandle(
- const sp<InputApplicationHandle>& inputApplicationHandle, jweak objWeak) :
- InputWindowHandle(inputApplicationHandle),
+NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) :
mObjWeak(objWeak) {
}
@@ -86,13 +85,12 @@
mInfo.touchableRegion.clear();
- jobject inputChannelObj = env->GetObjectField(obj,
- gInputWindowHandleClassInfo.inputChannel);
- if (inputChannelObj) {
- mInfo.inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
- env->DeleteLocalRef(inputChannelObj);
+ jobject tokenObj = env->GetObjectField(obj,
+ gInputWindowHandleClassInfo.token);
+ if (tokenObj) {
+ mInfo.token = ibinderForJavaObject(env, tokenObj);
} else {
- mInfo.inputChannel.clear();
+ mInfo.token.clear();
}
jstring nameObj = jstring(env->GetObjectField(obj,
@@ -120,7 +118,9 @@
gInputWindowHandleClassInfo.frameRight);
mInfo.frameBottom = env->GetIntField(obj,
gInputWindowHandleClassInfo.frameBottom);
- mInfo.scaleFactor = env->GetFloatField(obj,
+ mInfo.surfaceInset = env->GetIntField(obj,
+ gInputWindowHandleClassInfo.surfaceInset);
+ mInfo.globalScaleFactor = env->GetFloatField(obj,
gInputWindowHandleClassInfo.scaleFactor);
jobject regionObj = env->GetObjectField(obj,
@@ -155,6 +155,18 @@
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
+ jobject inputApplicationHandleObj = env->GetObjectField(obj,
+ gInputWindowHandleClassInfo.inputApplicationHandle);
+ if (inputApplicationHandleObj) {
+ sp<InputApplicationHandle> inputApplicationHandle =
+ android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
+ if (inputApplicationHandle != nullptr) {
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
+ }
+ env->DeleteLocalRef(inputApplicationHandleObj);
+ }
+
env->DeleteLocalRef(obj);
return true;
}
@@ -175,14 +187,8 @@
if (ptr) {
handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
} else {
- jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj,
- gInputWindowHandleClassInfo.inputApplicationHandle);
- sp<InputApplicationHandle> inputApplicationHandle =
- android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
- env->DeleteLocalRef(inputApplicationHandleObj);
-
jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
- handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);
+ handle = new NativeInputWindowHandle(objWeak);
handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
reinterpret_cast<jlong>(handle));
@@ -236,8 +242,8 @@
clazz,
"inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
- GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz,
- "inputChannel", "Landroid/view/InputChannel;");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
+ "token", "Landroid/os/IBinder;");
GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz,
"name", "Ljava/lang/String;");
@@ -263,6 +269,9 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz,
"frameBottom", "I");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
+ "surfaceInset", "I");
+
GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz,
"scaleFactor", "F");
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 2be267e..54b89f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -26,8 +26,7 @@
class NativeInputWindowHandle : public InputWindowHandle {
public:
- NativeInputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
- jweak objWeak);
+ NativeInputWindowHandle(jweak objWeak);
virtual ~NativeInputWindowHandle();
jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
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 7410b52..adab8e2 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -175,6 +175,17 @@
jmethodID postRecordConfigEventFromNative;
} gAudioPolicyEventHandlerMethods;
+//
+// JNI Initialization for OpenSLES routing
+//
+jmethodID gMidAudioTrackRoutingProxy_ctor;
+jmethodID gMidAudioTrackRoutingProxy_release;
+jmethodID gMidAudioRecordRoutingProxy_ctor;
+jmethodID gMidAudioRecordRoutingProxy_release;
+
+jclass gClsAudioTrackRoutingProxy;
+jclass gClsAudioRecordRoutingProxy;
+
static Mutex gLock;
enum AudioError {
@@ -2017,6 +2028,39 @@
return (jint)nativeToJavaStatus(status);
}
+static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) {
+ return FCC_8;
+}
+
+static jint
+android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid)
+{
+ status_t status = AudioSystem::setAssistantUid(uid);
+ return (jint)nativeToJavaStatus(status);
+}
+
+static jint
+android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) {
+ std::vector<uid_t> nativeUidsVector;
+
+ if (uids != nullptr) {
+ jsize len = env->GetArrayLength(uids);
+
+ if (len > 0) {
+ int *nativeUids = nullptr;
+ nativeUids = env->GetIntArrayElements(uids, 0);
+ if (nativeUids != nullptr) {
+ for (size_t i = 0; i < len; i++) {
+ nativeUidsVector.push_back(nativeUids[i]);
+ }
+ env->ReleaseIntArrayElements(uids, nativeUids, 0);
+ }
+ }
+ }
+ status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector);
+ return (jint)nativeToJavaStatus(status);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2077,9 +2121,10 @@
{"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
{"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
{"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
+ {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+ {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
};
-
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",
"(Ljava/lang/Object;)V",
@@ -2089,8 +2134,15 @@
(void *)android_media_AudioSystem_eventHandlerFinalize},
};
+static const JNINativeMethod gGetFCC8Methods[] = {
+ {"native_get_FCC_8", "()I", (void *)android_media_AudioSystem_get_FCC_8},
+};
+
int register_android_media_AudioSystem(JNIEnv *env)
{
+ // This needs to be done before hooking up methods AudioTrackRoutingProxy (below)
+ RegisterMethodsOrDie(env, kClassPathName, gGetFCC8Methods, NELEM(gGetFCC8Methods));
+
jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
@@ -2247,6 +2299,28 @@
gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env,
audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
+ // AudioTrackRoutingProxy methods
+ gClsAudioTrackRoutingProxy =
+ android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy");
+ // make sure this reference doesn't get deleted
+ gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy);
+
+ gMidAudioTrackRoutingProxy_ctor =
+ android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V");
+ gMidAudioTrackRoutingProxy_release =
+ android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "native_release", "()V");
+
+ // AudioRecordRoutingProxy
+ gClsAudioRecordRoutingProxy =
+ android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy");
+ // make sure this reference doesn't get deleted
+ gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy);
+
+ gMidAudioRecordRoutingProxy_ctor =
+ android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V");
+ gMidAudioRecordRoutingProxy_release =
+ android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
+
AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index bf22dd2..d927972 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1226,10 +1226,6 @@
pJniStorage->mDeviceCallback.clear();
}
-static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
- return FCC_8;
-}
-
// Pass through the arguments to the AudioFlinger track implementation.
static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
jobject jconfig, jobject joperation) {
@@ -1288,6 +1284,17 @@
}
// ----------------------------------------------------------------------------
+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
@@ -1351,7 +1358,6 @@
{"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
{"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
{"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
- {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8},
{"native_applyVolumeShaper",
"(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
(void *)android_media_AudioTrack_apply_volume_shaper},
@@ -1359,6 +1365,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},
};
@@ -1384,7 +1391,6 @@
}
}
-
// ----------------------------------------------------------------------------
int register_android_media_AudioTrack(JNIEnv *env)
{
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 49d5007..fa1da4b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,8 +33,6 @@
#include <iomanip>
#include <string>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
#include <debuggerd/client.h>
#include <log/log.h>
#include <utils/misc.h>
@@ -50,10 +48,6 @@
namespace android
{
-static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
- return UniqueFile(fopen(path, mode), safeFclose);
-}
-
enum {
HEAP_UNKNOWN,
HEAP_DALVIK,
diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h
index 81270ca..c7b731b 100644
--- a/core/jni/android_os_Debug.h
+++ b/core/jni/android_os_Debug.h
@@ -19,6 +19,8 @@
#include <memory>
#include <stdio.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
namespace android {
@@ -27,6 +29,11 @@
}
using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>;
+
+inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
+ return UniqueFile(fopen(path, mode), safeFclose);
+}
+
UniqueFile OpenSmapsOrRollup(int pid);
} // namespace android
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_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 8885aac..c6ea4e1 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -25,9 +25,10 @@
#include "unicode/uchar.h"
#define PROPERTY_UNDEFINED (-1)
+#define JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY 18
// ICU => JDK mapping
-static int directionality_map[U_CHAR_DIRECTION_COUNT] = {
+static int directionality_map[JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + 1] = {
0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0)
1, // U_RIGHT_TO_LEFT (1) => DIRECTIONALITY_RIGHT_TO_LEFT (1)
3, // U_EUROPEAN_NUMBER (2) => DIRECTIONALITY_EUROPEAN_NUMBER (3)
@@ -75,7 +76,8 @@
int c = 0x00010000 + ((src[i] - 0xD800) << 10) +
(src[i + 1] & 0x3FF);
int dir = u_charDirection(c);
- if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+ if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+ || u_charType(c) == U_UNASSIGNED)
dir = PROPERTY_UNDEFINED;
else
dir = directionality_map[dir];
@@ -85,7 +87,8 @@
} else {
int c = src[i];
int dir = u_charDirection(c);
- if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+ if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+ || u_charType(c) == U_UNASSIGNED)
dest[i] = PROPERTY_UNDEFINED;
else
dest[i] = directionality_map[dir];
@@ -96,7 +99,7 @@
static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
{
int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH))
width = PROPERTY_UNDEFINED;
return width;
@@ -121,6 +124,7 @@
return;
}
+ int maxWidth = u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH);
for (int i = 0; i < count; i++) {
const int srci = start + i;
if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF &&
@@ -129,7 +133,7 @@
int c = 0x00010000 + ((src[srci] - 0xD800) << 10) +
(src[srci + 1] & 0x3FF);
int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > maxWidth)
width = PROPERTY_UNDEFINED;
dest[i++] = width;
@@ -137,7 +141,7 @@
} else {
int c = src[srci];
int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > maxWidth)
width = PROPERTY_UNDEFINED;
dest[i] = width;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4c7defb..377e65c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1128,6 +1128,39 @@
return pss * 1024;
}
+static jlongArray android_os_Process_getRss(JNIEnv* env, jobject clazz, jint pid)
+{
+ // total, file, anon, swap
+ jlong rss[4] = {0, 0, 0, 0};
+ std::string status_path =
+ android::base::StringPrintf("/proc/%d/status", pid);
+ UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
+
+ char line[256];
+ while (fgets(line, sizeof(line), file.get())) {
+ jlong v;
+ if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) {
+ rss[0] = v;
+ } else if ( sscanf(line, "RssFile: %" SCNd64 " kB", &v) == 1) {
+ rss[1] = v;
+ } else if ( sscanf(line, "RssAnon: %" SCNd64 " kB", &v) == 1) {
+ rss[2] = v;
+ } else if ( sscanf(line, "VmSwap: %" SCNd64 " kB", &v) == 1) {
+ rss[3] = v;
+ }
+ }
+
+ jlongArray rssArray = env->NewLongArray(4);
+ if (rssArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ env->SetLongArrayRegion(rssArray, 0, 4, rss);
+
+ return rssArray;
+}
+
jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz,
jobjectArray commandNames)
{
@@ -1253,6 +1286,7 @@
{"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine},
{"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime},
{"getPss", "(I)J", (void*)android_os_Process_getPss},
+ {"getRss", "(I)[J", (void*)android_os_Process_getRss},
{"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands},
//{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
{"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index e89b593..752624b 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -468,6 +468,10 @@
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
}
+static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId();
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - Animations
// ----------------------------------------------------------------------------
@@ -694,6 +698,7 @@
{ "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
{ "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
{ "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark },
+ { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId },
};
int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ec9c860..ea6e017 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -377,6 +377,14 @@
transaction->setCrop_legacy(ctrl, crop);
}
+static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jfloat cornerRadius) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ transaction->setCornerRadius(ctrl, cornerRadius);
+}
+
static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint layerStack) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -883,6 +891,8 @@
(void*)nativeSetFlags },
{"nativeSetWindowCrop", "(JJIIII)V",
(void*)nativeSetWindowCrop },
+ {"nativeSetCornerRadius", "(JJF)V",
+ (void*)nativeSetCornerRadius },
{"nativeSetLayerStack", "(JJI)V",
(void*)nativeSetLayerStack },
{"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8e9a0bf..0286730 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,36 @@
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");
+ }
+ }
+
+ // We don't want core dumps, though, so 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 +991,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/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3a908dc..fd64c65 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,8 @@
// OPEN: Settings > Privacy
TOP_LEVEL_PRIVACY = 1587;
+
+ // OPEN: Settings > Developer options > Disable > Info dialog
+ DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
}
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 7de8020..ca8da55 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -222,6 +222,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 ];
@@ -550,6 +557,10 @@
}
optional MultiSim multi_sim = 76;
+ // Whether we've enabled native flags health check on this device. Takes effect on
+ // reboot. The value "1" enables native flags health check; otherwise it's disabled.
+ optional SettingProto native_flags_health_check_enabled = 144 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
message Netstats {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -624,6 +635,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;
@@ -865,6 +879,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;
@@ -987,5 +1003,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 144;
+ // Next tag = 146;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6e661e1..0e052fe 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -234,6 +234,18 @@
}
optional Location location = 31;
+ // How frequently will the user be reminded about location permission grants
+ message LocationAccessCheck {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ // Time in between periodic checks
+ optional SettingProto interval_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ // Time in between the user granting a location permission and a check
+ optional SettingProto delay_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional LocationAccessCheck location_access_check = 73;
+
message LockScreen {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -515,5 +527,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 73;
+ // Next tag = 74;
}
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..231caab 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -215,6 +215,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 +385,65 @@
}
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;
+ optional int32 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 are currently running. Valid only if the device is_active is true.
+ optional int32 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 +490,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 +692,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 +718,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 +752,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 +767,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/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 99f096d..3767ed5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -156,7 +156,7 @@
optional int32 rotation = 11;
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
- optional int32 surface_size = 14;
+ optional int32 surface_size = 14 [deprecated=true];
optional string focused_app = 15;
optional AppTransitionProto app_transition = 16;
}
@@ -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/runtime.proto b/core/proto/android/service/runtime.proto
new file mode 100644
index 0000000..ecbccef
--- /dev/null
+++ b/core/proto/android/service/runtime.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.service.runtime;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "RuntimeServiceProto";
+
+// Represents dumpsys info from RuntimeService.
+message RuntimeServiceInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Generic debug information to include.
+ repeated DebugEntryProto debug_entry = 1;
+}
+
+// A piece of key / value debug information.
+message DebugEntryProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string key = 1;
+
+ optional string string_value = 2;
+}
diff --git a/core/proto/android/stats/devicepolicy/Android.bp b/core/proto/android/stats/devicepolicy/Android.bp
new file mode 100644
index 0000000..6ae54e2
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+ name: "devicepolicyprotosnano",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ "*.proto",
+ ],
+ java_version: "1.8",
+ target: {
+ android: {
+ jarjar_rules: "jarjar-rules.txt",
+ },
+ host: {
+ static_libs: ["libprotobuf-java-nano"],
+ }
+ },
+ no_framework_libs: true,
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/proto/android/stats/devicepolicy/device_policy.proto
similarity index 79%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/proto/android/stats/devicepolicy/device_policy.proto
index c66a6cb..af30cf3 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/proto/android/stats/devicepolicy/device_policy.proto
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.view.intelligence;
+syntax = "proto2";
-parcelable ContentCaptureEvent;
+package android.stats.devicepolicy;
+option java_multiple_files = true;
+
+message StringList {
+ repeated string string_value = 1;
+}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
new file mode 100644
index 0000000..5726d9a
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+option java_multiple_files = true;
+
+/**
+ * Id for device policy features.
+ */
+enum EventId {
+ SET_PASSWORD_QUALITY = 1;
+ SET_PASSWORD_MINIMUM_LENGTH = 2;
+ SET_PASSWORD_MINIMUM_NUMERIC = 3;
+ SET_PASSWORD_MINIMUM_NON_LETTER = 4;
+ SET_PASSWORD_MINIMUM_LETTERS = 5;
+ SET_PASSWORD_MINIMUM_LOWER_CASE = 6;
+ SET_PASSWORD_MINIMUM_UPPER_CASE = 7;
+ SET_PASSWORD_MINIMUM_SYMBOLS = 8;
+ SET_KEYGUARD_DISABLED_FEATURES = 9;
+ LOCK_NOW = 10;
+ WIPE_DATA_WITH_REASON = 11;
+ ADD_USER_RESTRICTION = 12;
+ REMOVE_USER_RESTRICTION = 13;
+ SET_SECURE_SETTING = 14;
+ SET_SECURITY_LOGGING_ENABLED = 15;
+ RETRIEVE_SECURITY_LOGS = 16;
+ RETRIEVE_PRE_REBOOT_SECURITY_LOGS = 17;
+ SET_PERMISSION_POLICY = 18;
+ SET_PERMISSION_GRANT_STATE = 19;
+ INSTALL_KEY_PAIR = 20;
+ INSTALL_CA_CERT = 21;
+ 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_CAMERA_DISABLED = 30;
+ QUERY_SUMMARY_FOR_USER = 31;
+ QUERY_SUMMARY = 32;
+ QUERY_DETAILS = 33;
+ REBOOT = 34;
+ SET_MASTER_VOLUME_MUTED = 35;
+ SET_AUTO_TIME_REQUIRED = 36;
+ SET_KEYGUARD_DISABLED = 37;
+ SET_STATUS_BAR_DISABLED = 38;
+ SET_ORGANIZATION_COLOR = 39;
+ SET_PROFILE_NAME = 40;
+ SET_USER_ICON = 41;
+ SET_DEVICE_OWNER_LOCKSCREEN_INFO = 42;
+ SET_SHORT_SUPPORT_MESSAGE = 43;
+ SET_LONG_SUPPORT_MESSAGE = 44;
+ SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45;
+ 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_MODE_ENABLED = 51;
+ ADD_PERSISTENT_PREFERRED_ACTIVITY = 52;
+ REQUEST_BUGREPORT = 53;
+ GET_WIFI_MAC_ADDRESS = 54;
+ REQUEST_QUIET_MODE_ENABLED = 55;
+ WORK_PROFILE_LOCATION_CHANGED = 56;
+ DO_USER_INFO_CLICKED = 57;
+ TRANSFER_OWNERSHIP = 58;
+ GENERATE_KEY_PAIR = 59;
+ SET_KEY_PAIR_CERTIFICATE = 60;
+ SET_KEEP_UNINSTALLED_PACKAGES = 61;
+ SET_APPLICATION_RESTRICTIONS = 62;
+ SET_APPLICATION_HIDDEN = 63;
+ ENABLE_SYSTEM_APP = 64;
+ ENABLE_SYSTEM_APP_WITH_INTENT = 65;
+ INSTALL_EXISTING_PACKAGE = 66;
+ SET_UNINSTALL_BLOCKED = 67;
+ SET_PACKAGES_SUSPENDED = 68;
+ ON_LOCK_TASK_MODE_ENTERING = 69;
+ ADD_CROSS_PROFILE_CALENDAR_PACKAGE = 70;
+ REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE = 71;
+ GET_USER_PASSWORD_COMPLEXITY_LEVEL = 72;
+ INSTALL_SYSTEM_UPDATE = 73;
+ INSTALL_SYSTEM_UPDATE_ERROR = 74;
+ IS_MANAGED_KIOSK = 75;
+ IS_UNATTENDED_MANAGED_KIOSK = 76;
+ 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_CLOUD_ENROLLMENT = 81;
+ PROVISIONING_ENTRY_POINT_ADB = 82;
+ PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83;
+ PROVISIONING_DPC_PACKAGE_NAME = 84;
+ PROVISIONING_DPC_INSTALLED_BY_PACKAGE = 85;
+ PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS = 86;
+ PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
+ PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
+ PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
+ PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
+ PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
+ PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+ PROVISIONING_NETWORK_TYPE = 93;
+ PROVISIONING_ACTION = 94;
+ PROVISIONING_EXTRAS = 95;
+ PROVISIONING_COPY_ACCOUNT_TASK_MS = 96;
+ PROVISIONING_CREATE_PROFILE_TASK_MS = 97;
+ PROVISIONING_START_PROFILE_TASK_MS = 98;
+ PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS = 99;
+ PROVISIONING_INSTALL_PACKAGE_TASK_MS = 100;
+ PROVISIONING_CANCELLED = 101;
+ PROVISIONING_ERROR = 102;
+ PROVISIONING_COPY_ACCOUNT_STATUS = 103;
+ PROVISIONING_TOTAL_TASK_TIME_MS = 104;
+ PROVISIONING_SESSION_STARTED = 105;
+ PROVISIONING_SESSION_COMPLETED = 106;
+ 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;
+}
diff --git a/core/proto/android/stats/devicepolicy/jarjar-rules.txt b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
new file mode 100644
index 0000000..40043a86
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index fba2e51..4777169 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -51,6 +51,7 @@
NETWORK_TYPE_TD_SCDMA = 17;
NETWORK_TYPE_IWLAN = 18;
NETWORK_TYPE_LTE_CA = 19;
+ NETWORK_TYPE_NR = 20;
}
// Signal strength levels, primarily used by android/telephony/SignalStrength.java.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e35b701..594ae6b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -620,11 +620,20 @@
<protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
<eat-comment />
+ <!-- Grouping for platform runtime permissions is not accessible to apps
+ @hide
+ @SystemApi
+ -->
+ <permission-group android:name="android.permission-group.UNDEFINED"
+ android:priority="100" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing user's contacts including personal profile -->
<!-- ====================================================================== -->
@@ -643,14 +652,17 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts"
android:protectionLevel="dangerous" />
@@ -672,14 +684,17 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCalendar"
android:description="@string/permdesc_readCalendar"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCalendar"
android:description="@string/permdesc_writeCalendar"
android:protectionLevel="dangerous" />
@@ -701,6 +716,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.SEND_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sendSms"
android:description="@string/permdesc_sendSms"
android:permissionFlags="costsMoney"
@@ -710,33 +726,41 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveSms"
android:description="@string/permdesc_receiveSms"
- android:protectionLevel="dangerous"/>
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readSms"
android:description="@string/permdesc_readSms"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveWapPush"
android:description="@string/permdesc_receiveWapPush"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to monitor incoming MMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_MMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveMms"
android:description="@string/permdesc_receiveMms"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
@@ -751,9 +775,11 @@
<p>Protection level: dangerous
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCellBroadcasts"
android:description="@string/permdesc_readCellBroadcasts"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
@@ -792,9 +818,11 @@
@deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
- android:protectionLevel="normal" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -812,9 +840,11 @@
@deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
- android:protectionLevel="normal" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Runtime permission controlling access to the user's shared aural media
collection. -->
@@ -827,15 +857,11 @@
<!-- Allows an application to read the user's shared audio collection. -->
<permission android:name="android.permission.READ_MEDIA_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_audioRead"
android:description="@string/permdesc_audioRead"
- android:protectionLevel="dangerous" />
-
- <!-- Allows an application to modify the user's shared audio collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_AUDIO"
- android:label="@string/permlab_audioWrite"
- android:description="@string/permdesc_audioWrite"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Runtime permission controlling access to the user's shared visual media
collection, including images and videos. -->
@@ -848,34 +874,28 @@
<!-- Allows an application to read the user's shared images collection. -->
<permission android:name="android.permission.READ_MEDIA_IMAGES"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_imagesRead"
android:description="@string/permdesc_imagesRead"
- android:protectionLevel="dangerous" />
-
- <!-- Allows an application to modify the user's shared images collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_IMAGES"
- android:label="@string/permlab_imagesWrite"
- android:description="@string/permdesc_imagesWrite"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to read the user's shared video collection. -->
<permission android:name="android.permission.READ_MEDIA_VIDEO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_videoRead"
android:description="@string/permdesc_videoRead"
- android:protectionLevel="dangerous" />
-
- <!-- Allows an application to modify the user's shared video collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_VIDEO"
- android:label="@string/permlab_videoWrite"
- android:description="@string/permdesc_videoWrite"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to access any geographic locations persisted in the
user's shared collection. -->
<permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_mediaLocation"
android:description="@string/permdesc_mediaLocation"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- @hide @SystemApi
Allows an application to modify OBB files visible to other apps. -->
@@ -903,20 +923,24 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an app to access approximate location.
Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an app to access location in the background. If you
are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
@@ -925,9 +949,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessBackgroundLocation"
android:description="@string/permdesc_accessBackgroundLocation"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the call log -->
@@ -965,9 +991,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCallLog"
android:description="@string/permdesc_readCallLog"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write (but not read) the user's
call log data.
@@ -983,6 +1011,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCallLog"
android:description="@string/permdesc_writeCallLog"
android:protectionLevel="dangerous" />
@@ -993,9 +1022,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_processOutgoingCalls"
android:description="@string/permdesc_processOutgoingCalls"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -1024,23 +1055,28 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
<p>Protection level: dangerous-->
<permission android:name="android.permission.READ_PHONE_NUMBERS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneNumbers"
android:description="@string/permdesc_readPhoneNumbers"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:permissionFlags="costsMoney"
android:label="@string/permlab_callPhone"
android:description="@string/permdesc_callPhone"
@@ -1050,6 +1086,7 @@
<p>Protection level: dangerous
-->
<permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_addVoicemail"
android:description="@string/permdesc_addVoicemail"
android:protectionLevel="dangerous" />
@@ -1058,6 +1095,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.USE_SIP"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip"
android:protectionLevel="dangerous"/>
@@ -1066,6 +1104,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ANSWER_PHONE_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_answerPhoneCalls"
android:description="@string/permdesc_answerPhoneCalls"
android:protectionLevel="dangerous|runtime" />
@@ -1093,6 +1132,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCEPT_HANDOVER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android.label="@string/permlab_acceptHandover"
android:description="@string/permdesc_acceptHandovers"
android:protectionLevel="dangerous" />
@@ -1116,9 +1156,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECORD_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordAudio"
android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous|instant"/>
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for activity recognition -->
@@ -1137,9 +1179,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACTIVITY_RECOGNITION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_activityRecognition"
android:description="@string/permdesc_activityRecognition"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
@@ -1185,9 +1229,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
@@ -1208,9 +1254,11 @@
measure what is happening inside his/her body, such as heart rate.
<p>Protection level: dangerous -->
<permission android:name="android.permission.BODY_SENSORS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_bodySensors"
android:description="@string/permdesc_bodySensors"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@@ -1694,9 +1742,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.GET_ACCOUNTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="dangerous"
android:description="@string/permdesc_getAccounts"
- android:label="@string/permlab_getAccounts" />
+ android:label="@string/permlab_getAccounts"
+ android:usageInfoRequired="true" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- @SystemApi Allows applications to call into AccountAuthenticators.
@@ -2997,12 +3047,20 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a android.service.intelligence.IntelligenceService,
+ <!-- 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_INTELLIGENCE_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,
@@ -3024,6 +3082,13 @@
<permission android:name="android.permission.BIND_TV_INPUT"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi
Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
to ensure that only the system can bind to it.
@@ -3330,6 +3395,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
@@ -4113,6 +4185,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> -->
@@ -4213,6 +4290,28 @@
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
+ <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+ {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
+ intents}. -->
+ <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi Allows requesting the framework broadcast the
+ {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
+ @hide -->
+ <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY}
+ intent.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|preinstalled" />
+ <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
+ @hide -->
+ <permission android:name="android.permission.AMBIENT_WALLPAPER"
+ android:protectionLevel="signature|preinstalled" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -4500,6 +4599,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.ConversationActionsInstallReceiver"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.ACTION_UPDATE_CONVERSATION_ACTIONS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.CarrierIdInstallReceiver"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
@@ -4524,6 +4631,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_action.xml b/core/res/res/layout/notification_material_action.xml
index 3c9f6ee..c024dbe 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -16,16 +16,14 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ style="@android:style/NotificationAction"
android:id="@+id/action0"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center"
android:gravity="start|center_vertical"
android:layout_marginStart="4dp"
- android:textColor="@color/notification_default_color"
android:singleLine="true"
android:textAlignment="viewStart"
android:ellipsize="end"
- android:background="@drawable/notification_material_action_background"
/>
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 07559f4..4258019 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -18,6 +18,7 @@
android:id="@+id/actions_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_action_list_margin_top"
android:layout_gravity="bottom">
<com.android.internal.widget.NotificationActionListLayout
android:id="@+id/actions"
@@ -27,6 +28,7 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:visibility="gone"
+ android:background="@color/notification_action_list_background_color"
>
<!-- actions will be added here -->
</com.android.internal.widget.NotificationActionListLayout>
diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml
index 9fa7c6a..f165724 100644
--- a/core/res/res/layout/notification_material_action_tombstone.xml
+++ b/core/res/res/layout/notification_material_action_tombstone.xml
@@ -16,7 +16,7 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ style="@android:style/NotificationTombstoneAction"
android:id="@+id/action0"
android:layout_width="wrap_content"
android:layout_height="48dp"
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 433ae39..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"
@@ -126,7 +126,6 @@
android:visibility="gone"
android:contentDescription="@string/notification_alerted_content_description"
android:src="@drawable/ic_notifications_alerted"
- android:tint="@color/notification_secondary_text_color_light"
/>
<ImageView
android:id="@+id/profile_badge"
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..56c2e3e 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>
@@ -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..2af43ce 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,6 +529,11 @@
<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>
@@ -540,7 +541,6 @@
<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>
@@ -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..77a8f05 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>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ürituse ajastamine 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="translate" msgid="9218619809342576858">"Tõlgi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tõlkige valitud tekst"</string>
+ <string name="define" msgid="7394820043869954211">"Defineeri"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Valitud teksti defineerimine"</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..bc367be 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>
@@ -1105,6 +1101,10 @@
<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..fccb289 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>
@@ -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..69b4db2 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>
@@ -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>
@@ -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..2232f4d 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>
@@ -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..eec4b31 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>
@@ -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..ac28270 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>
@@ -1105,6 +1101,10 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Aрга хэмжээг сонгосон цагт хуваарилах"</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/strings.xml b/core/res/res/values-mr/strings.xml
index ca7f6df..9a58b38 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>
@@ -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="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-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..39afd74 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>
@@ -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">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ 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..f606d77 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>
@@ -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-night/values.xml b/core/res/res/values-night/values.xml
index 9de8842..a2ad3b9 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
<!-- Color palette -->
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/white</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c1883fa..a6bd007 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>
@@ -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..1d1f3e7 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>
@@ -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="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..9a70f0b 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>
@@ -1145,6 +1141,10 @@
<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_desc" msgid="3876322502674253506">"Sledi izbranemu letu"</string>
+ <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevod izbranega besedila"</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..25171b8 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>
@@ -1105,6 +1101,10 @@
<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_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..492bad5 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>
@@ -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 605662a..8dbea38 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1915,6 +1915,9 @@
<enum name="KEYCODE_SYSTEM_NAVIGATION_RIGHT" value="283" />
<enum name="KEYCODE_ALL_APPS" value="284" />
<enum name="KEYCODE_REFRESH" value="285" />
+ <enum name="KEYCODE_THUMBS_UP" value="286" />
+ <enum name="KEYCODE_THUMBS_DOWN" value="287" />
+ <enum name="KEYCODE_PROFILE_SWITCH" value="288" />
</attr>
<!-- ***************************************************************** -->
@@ -5532,7 +5535,7 @@
<attr name="solidColor" format="color|reference" />
<!-- @hide The divider for making the selection area. -->
<attr name="selectionDivider" format="reference" />
- <!-- @hide The height of the selection divider. -->
+ <!-- The height of the selection divider. -->
<attr name="selectionDividerHeight" format="dimension" />
<!-- @hide The distance between the two selection dividers. -->
<attr name="selectionDividersDistance" format="dimension" />
@@ -6031,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 -->
@@ -7935,12 +7938,19 @@
wallpaper. -->
<attr name="showMetadataInPreview" format="boolean" />
- <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
+ <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
+ This feature requires the android.permission.AMBIENT_WALLPAPER permission.
+ @hide @SystemApi -->
<attr name="supportsAmbientMode" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
<attr name="settingsSliceUri" />
+ <!-- Indicates that this wallpaper service can support multiple engines to render on each
+ surface independently. An example use case is a multi-display set-up where the
+ wallpaper service can render surfaces to each of the connected displays. -->
+ <attr name="supportsMultipleDisplays" format="boolean" />
+
</declare-styleable>
<!-- Use <code>dream</code> as the root tag of the XML resource that
@@ -8798,6 +8808,7 @@
<attr name="layout_ignoreOffset" format="boolean" />
<attr name="layout_gravity" />
<attr name="layout_hasNestedScrollIndicator" format="boolean" />
+ <attr name="layout_maxHeight" />
</declare-styleable>
<!-- @hide -->
@@ -8907,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
@@ -8939,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 6fc0f5b..089c59f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1601,6 +1601,10 @@
<attr name="request" />
<attr name="protectionLevel" />
<attr name="permissionFlags" />
+ <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage
+ attributes in their {@code uses-permission} elements or the permission will not be
+ granted. -->
+ <attr name="usageInfoRequired" format="boolean" />
</declare-styleable>
<!-- The <code>permission-group</code> tag declares a logical grouping of
@@ -1700,6 +1704,81 @@
requested. If it does support the feature, it will be as if the manifest didn't
request it at all. -->
<attr name="requiredNotFeature" format="string" />
+
+ <!-- Specify if the app uploads data, or derived data, guarded by this permission.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataSentOffDevice">
+ <!-- The application may send data, or derived data, guarded by this permission off of the
+ device. -->
+ <enum name="yes" value="1" />
+ <!-- The application may send data, or derived data, guarded by this permission off of the
+ device, however it will only do so when explicitly triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application does not send data, or derived data, guarded by this permission off
+ of the device. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify if the application or its related off-device services provide data,
+ or derived data, guarded by this permission to third parties outside of the developer's
+ organization that do not qualify as data processors.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataSharedWithThirdParty">
+ <!-- The application or its services may provide data, or derived data, guarded by this
+ permission to third party organizations. -->
+ <enum name="yes" value="1" />
+ <!-- The application or its services may provide data, or derived data, guarded by this
+ permission to third party organizations, however it will only do so when explicitly
+ triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application or its services does not provide data, or derived data, guarded by
+ this permission to third party organizations. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify if the application or its related off-device services use data,
+ or derived data, guarded by this permission for monetization purposes.
+
+ For example, if the data is sold to another party or used for targeting advertisements
+ this must be set to {@code yes}.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataUsedForMonetization">
+ <!-- The application or its services may use data, or derived data, guarded by this
+ permission for monetization purposes. -->
+ <enum name="yes" value="1" />
+ <!-- The application or its services may use data, or derived data, guarded by this
+ permission for monetization purposes, however it will only do so when explicity
+ triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application or its services does not use data, or derived data, guarded by
+ this permission for monetization purposes. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify how long the application or its related off-device services store
+ data, or derived data, guarded by this permission.
+
+ This can be one of "notRetained", "userSelected", "unlimited", or a number
+ representing the number of weeks the data is retained.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataRetentionTime" format="string" />
+
</declare-styleable>
<!-- The <code>uses-configuration</code> tag specifies
@@ -2148,6 +2227,21 @@
<attr name="visibleToInstantApps" />
<!-- The code for this component is located in the given split. -->
<attr name="splitName" />
+ <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
+ will be spawned from an Application Zygote, instead of the regular Zygote.
+ <p>
+ The Application Zygote will pre-initialize the application's class loader,
+ and call a static callback into the application to allow it to perform
+ application-specific preloads (such as loading a shared library). Therefore,
+ spawning from the Application Zygote will typically reduce the service
+ launch time and reduce its memory usage. The downside of using this flag
+ is that you will have an additional process (the app zygote itself) that
+ is taking up memory. Whether actual memory usage is improved therefore strongly
+ depends on the number of isolated services that an application starts,
+ and how much memory those services save by preloading. Therefore, it is
+ recommended to measure memory usage under typical workloads to determine
+ whether it makes sense to use this flag. -->
+ <attr name="useAppZygote" format="boolean" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4122cf0..16c0744 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -146,10 +146,14 @@
<color name="notification_default_color">#757575</color> <!-- Gray 600 -->
+ <color name="notification_action_button_text_color">@color/notification_default_color</color>
+
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
<color name="notification_action_list">#ffeeeeee</color>
+ <color name="notification_action_list_background_color">@null</color>
+
<!-- Keyguard colors -->
<color name="keyguard_avatar_frame_color">#ffffffff</color>
<color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 829d6f5..62ec5c4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,7 +364,7 @@
<!-- Max number of Bluetooth tethering connections allowed. If this is
updated config_tether_dhcp_range has to be updated appropriately. -->
- <integer translateable="false" name="config_max_pan_devices">5</integer>
+ <integer translatable="false" name="config_max_pan_devices">5</integer>
<!-- Dhcp range (min, max) to use for tethering purposes -->
<string-array translatable="false" name="config_tether_dhcp_range">
@@ -2125,6 +2125,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>
@@ -3347,6 +3350,22 @@
-->
<string name="config_defaultTextClassifierPackage" 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 +3503,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>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e902989..05a156b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -200,6 +200,9 @@
<!-- The height of the notification action list -->
<dimen name="notification_action_list_height">60dp</dimen>
+ <!-- The margin of the notification action list at the top -->
+ <dimen name="notification_action_list_margin_top">0dp</dimen>
+
<!-- The height of the notification action list -->
<dimen name="notification_action_emphasized_height">48dp</dimen>
@@ -427,7 +430,7 @@
<dimen name="notification_badge_size">12dp</dimen>
<!-- Size of the alerted icon for notifications -->
- <dimen name="notification_alerted_size">18dp</dimen>
+ <dimen name="notification_alerted_size">12dp</dimen>
<!-- Keyguard dimensions -->
<!-- TEMP -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 64e5bc0..bbe3ff9 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -190,4 +190,7 @@
<!-- A tag used to save the view added to a transition overlay -->
<item type="id" name="transition_overlay_view_tag" />
+
+ <!-- A tag used to save the notification action object -->
+ <item type="id" name="notification_action_index_tag" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73dae08..5e8af62 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,6 +2909,7 @@
<public name="opticalInsetRight" />
<public name="opticalInsetBottom" />
<public name="forceDarkAllowed" />
+ <!-- @hide @SystemApi -->
<public name="supportsAmbientMode" />
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="usesNonSdkApi" />
@@ -2920,6 +2921,14 @@
<public name="shell" />
<public name="interactiveUiTimeout" />
<public name="importantForContentCapture" />
+ <public name="supportsMultipleDisplays" />
+ <public name="useAppZygote" />
+ <public name="usageInfoRequired" />
+ <public name="dataSentOffDevice" />
+ <public name="dataSharedWithThirdParty" />
+ <public name="dataUsedForMonetization" />
+ <public name="dataRetentionTime" />
+ <public name="selectionDividerHeight" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 200c35d..a33f6b2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1444,9 +1444,14 @@
<!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
<string name="biometric_dialog_default_title">Application <xliff:g id="app" example="Gmail">%s</xliff:g> wants to authenticate.</string>
-
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
+ <!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
+ <string name="biometric_error_user_canceled">Authentication canceled</string>
+ <!-- Message shown by the biometric dialog when biometric is not recognized -->
+ <string name="biometric_not_recognized">Not recognized</string>
+ <!-- Message shown when biometric authentication has been canceled [CHAR LIMIT=50] -->
+ <string name="biometric_error_canceled">Authentication canceled</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
@@ -1462,8 +1467,6 @@
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <!-- Message shown by the biometric dialog when biometric is not recognized -->
- <string name="biometric_not_recognized">Not recognized</string>
<!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
<string name="fingerprint_authenticated">Fingerprint authenticated</string>
<!-- Accessibility message announced when a face has been authenticated [CHAR LIMIT=NONE] -->
@@ -1585,23 +1588,15 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permlab_sdcardRead" product="nosdcard">read the contents of your USB storage</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardRead" product="default">read the contents of your SD card</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to read the contents of your USB storage.</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardRead" product="default">Allows the app to read the contents of your SD card.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permlab_sdcardRead">read the contents of your shared storage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permlab_sdcardWrite" product="nosdcard">modify or delete the contents of your USB storage</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardWrite" product="default">modify or delete the contents of your SD card</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permdesc_sdcardWrite" product="nosdcard">Allows the app to write to the USB storage.</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
+ <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
+ <string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_use_sip">make/receive SIP calls</string>
@@ -2989,6 +2984,18 @@
<!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
<string name="view_flight_desc">Track selected flight</string>
+ <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
+ <string name="translate">Translate</string>
+
+ <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
+ <string name="translate_desc">Translate selected text</string>
+
+ <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
+ <string name="define">Define</string>
+
+ <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
+ <string name="define_desc">Define selected text</string>
+
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
@@ -3867,10 +3874,8 @@
<string name="action_mode_done">Done</string>
<!-- Strings for MasterClearReceiver. -->
- <!-- Text for progress dialog while erasing USB storage volume [CHAR LIMIT=NONE] -->
- <string name="progress_erasing" product="nosdcard">Erasing USB storage\u2026</string>
- <!-- Text for progress dialog while erasing SD card [CHAR LIMIT=NONE] -->
- <string name="progress_erasing" product="default">Erasing SD card\u2026</string>
+ <!-- Text for progress dialog while erasing the shared storage volume [CHAR LIMIT=NONE] -->
+ <string name="progress_erasing">Erasing shared storage\u2026</string>
<!-- Text for WebView's text selection Action Mode -->
<!-- ActionBar action to share the current selection [CHAR LIMIT=10] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bd53936..18f7e48 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1526,10 +1526,22 @@
<item name="gravity">top</item>
</style>
- <!-- Colored bordered ink button -->
+ <!-- The style for normal action button on notification -->
+ <style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
+ <item name="textColor">@color/notification_action_button_text_color</item>
+ <item name="background">@drawable/notification_material_action_background</item>
+ </style>
+
+ <!-- The style for emphasized action button on notification: Colored bordered ink button -->
<style name="NotificationEmphasizedAction" parent="Widget.Material.Button">
<item name="background">@drawable/btn_notification_emphasized</item>
<item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item>
</style>
+ <!-- The style for disabled action button on notification -->
+ <style name="NotificationTombstoneAction" parent="NotificationAction">
+ <item name="textColor">#555555</item>
+ </style>
+
+
</resources>
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 82c9ff3..82a679e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,10 @@
<java-symbol type="string" name="add_calendar_event_desc" />
<java-symbol type="string" name="view_flight" />
<java-symbol type="string" name="view_flight_desc" />
+ <java-symbol type="string" name="translate" />
+ <java-symbol type="string" name="translate_desc" />
+ <java-symbol type="string" name="define" />
+ <java-symbol type="string" name="define_desc" />
<java-symbol type="string" name="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -2402,7 +2406,9 @@
<!-- Biometric messages -->
<java-symbol type="string" name="biometric_dialog_default_title" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
+ <java-symbol type="string" name="biometric_error_user_canceled" />
<java-symbol type="string" name="biometric_not_recognized" />
+ <java-symbol type="string" name="biometric_error_canceled" />
<!-- Fingerprint messages -->
<java-symbol type="string" name="fingerprint_error_unable_to_process" />
@@ -2679,6 +2685,7 @@
<java-symbol type="id" name="smart_reply_container" />
<java-symbol type="id" name="remote_input_tag" />
<java-symbol type="id" name="pending_intent_tag" />
+ <java-symbol type="id" name="notification_action_index_tag" />
<java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
<java-symbol type="string" name="ext_media_status_removed" />
@@ -3258,6 +3265,8 @@
<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_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" />
@@ -3303,6 +3312,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" />
@@ -3494,4 +3504,8 @@
<java-symbol type="string" name="config_defaultAssistantComponentName" />
<java-symbol type="id" name="transition_overlay_view_tag" />
+
+ <java-symbol type="dimen" name="rounded_corner_radius" />
+ <java-symbol type="dimen" name="rounded_corner_radius_top" />
+ <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
</resources>
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/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f60d8d0ad..46d4a47 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -91,6 +91,8 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.KILL_UID" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+
<!-- location test permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
@@ -1316,6 +1318,12 @@
<service android:name="android.os.BinderThreadPriorityService"
android:process=":BinderThreadPriorityService" />
+ <!-- Used by BinderWorkSourceTest -->
+ <service android:name="android.os.BinderWorkSourceService"
+ android:process=":BinderWorkSourceService" />
+ <service android:name="android.os.BinderWorkSourceNestedService"
+ android:process=":BinderWorkSourceNestedService" />
+
<!-- Application components used for search manager tests -->
<activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e1cb911..e89a4d3 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -106,9 +106,9 @@
int backgroundColor = 0xff585868;
int initialForegroundColor = 0xff505868;
builder.setColorPalette(backgroundColor, initialForegroundColor);
- int primaryTextColor = builder.getPrimaryTextColor();
+ int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
- int secondaryTextColor = builder.getSecondaryTextColor();
+ int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a317c99..8ac9451d 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -364,9 +364,7 @@
private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
// Flags explicitly over ride everything else.
- if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- return INSTALL_LOC_SD;
- } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+ if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
return INSTALL_LOC_INT;
}
// Manifest option takes precedence next
@@ -437,8 +435,6 @@
int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
if (rLoc == INSTALL_LOC_INT) {
- assertFalse(
- (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
assertEquals(appInstallPath, srcPath);
assertEquals(appInstallPath, publicSrcPath);
assertStartsWith("Native library should point to shared lib directory",
@@ -461,8 +457,6 @@
}
}
} else if (rLoc == INSTALL_LOC_SD) {
- assertFalse("The application should not be installed forward locked",
- (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
assertTrue("Application flags (" + info.flags
+ ") should contain FLAG_EXTERNAL_STORAGE",
(info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -845,31 +839,10 @@
}
@LargeTest
- public void testReplaceFailSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
- }
-
- @LargeTest
public void testReplaceNormalInternal() throws Exception {
sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
}
- @LargeTest
- public void testReplaceSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING
- | PackageManager.INSTALL_EXTERNAL);
- }
-
/* -------------- Delete tests --- */
private static class DeleteObserver extends IPackageDeleteObserver.Stub {
private CountDownLatch mLatch = new CountDownLatch(1);
@@ -1015,31 +988,12 @@
deleteFromRawResource(0, 0);
}
- @LargeTest
- public void testDeleteSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
- }
@LargeTest
public void testDeleteNormalInternalRetainData() throws Exception {
deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
}
- @LargeTest
- public void testDeleteSdcardRetainData() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
- }
-
void cleanUpInstall(InstallParams ip) throws Exception {
if (ip == null) {
return;
@@ -1104,60 +1058,6 @@
0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
}
- /*
- * Install a package on internal flash via PackageManager install flag. Replace
- * the package via flag to install on sdcard. Make sure the new flag overrides
- * the old install location.
- */
- @LargeTest
- public void testReplaceFlagInternalSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = 0;
- int rFlags = PackageManager.INSTALL_EXTERNAL;
- InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
- int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
- try {
- invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
- assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
- } finally {
- cleanUpInstall(ip);
- }
- }
-
- /*
- * Install a package on sdcard via PackageManager install flag. Replace
- * the package with no flags or manifest option and make sure the old
- * install location is retained.
- */
- @LargeTest
- public void testReplaceFlagSdcardInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = 0;
- InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
- int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
- try {
- invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
- } finally {
- cleanUpInstall(ip);
- }
- }
-
@LargeTest
public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
// Do not run on devices with emulated external storage.
@@ -1375,34 +1275,6 @@
}
@LargeTest
- public void testMoveAppExternalToExternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int installFlags = PackageManager.INSTALL_EXTERNAL;
- int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
- boolean fail = true;
- int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
- }
-
- @LargeTest
- public void testMoveAppExternalToInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int installFlags = PackageManager.INSTALL_EXTERNAL;
- int moveFlags = PackageManager.MOVE_INTERNAL;
- boolean fail = false;
- int result = PackageManager.MOVE_SUCCEEDED;
- sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
- }
-
- @LargeTest
public void testMoveAppFailInternalToExternalDelete() throws Exception {
// Do not run on devices with emulated external storage.
if (Environment.isExternalStorageEmulated()) {
@@ -1458,19 +1330,6 @@
}
/*
- * Install an app on sdcard.
- */
- @LargeTest
- public void testFlagE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
- }
-
- /*
* Install an app with both internal and manifest option set.
* should install on internal.
*/
@@ -1506,59 +1365,6 @@
false, -1,
PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
- /*
- * Install an app with both external and manifest option set.
- * should install externally.
- */
- @LargeTest
- public void testFlagEManifestI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_internal,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- /*
- * Install an app with both external and manifest preference for
- * preferExternal. Should install externally.
- */
- @LargeTest
- public void testFlagEManifestE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- /*
- * Install an app with both external and manifest preference for
- * auto. should install on external media.
- */
- @LargeTest
- public void testFlagEManifestA() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_auto,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
/*
* The following test functions verify install location for existing apps.
@@ -1586,75 +1392,6 @@
-1);
}
- @LargeTest
- public void testFlagIExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
- @LargeTest
- public void testFlagEExistingI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_INTERNAL;
- int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
- @LargeTest
- public void testFlagEExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
/*
* The following set of tests verify the installation of apps with
* install location attribute set to internalOnly, preferExternal and auto.
@@ -1720,29 +1457,6 @@
}
@LargeTest
- public void testManifestIExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_internal,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- }
-
- @LargeTest
public void testManifestEExistingI() throws Exception {
// Do not run on devices with emulated external storage.
if (Environment.isExternalStorageEmulated()) {
@@ -1766,29 +1480,6 @@
}
@LargeTest
- public void testManifestEExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
public void testManifestAExistingI() throws Exception {
int iFlags = PackageManager.INSTALL_INTERNAL;
int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
@@ -1806,29 +1497,6 @@
PackageInfo.INSTALL_LOCATION_AUTO);
}
- @LargeTest
- public void testManifestAExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_auto,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
/*
* The following set of tests check install location for existing
* application based on user setting.
@@ -1896,42 +1564,6 @@
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
- @LargeTest
- public void testExistingEUserI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
- public void testExistingEUserE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
- public void testExistingEUserA() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
/*
* The following set of tests verify that the user setting defines
* the install location.
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
new file mode 100644
index 0000000..dddeda3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+* Service used by {@link BinderWorkSourceTest}.
+*/
+public class BinderWorkSourceNestedService extends Service {
+ private final IBinderWorkSourceNestedService.Stub mBinder =
+ new IBinderWorkSourceNestedService.Stub() {
+
+ public int[] nestedCallWithWorkSourceToSet(int uidToBlame) {
+ final int uid = Binder.getCallingWorkSourceUid();
+ if (uidToBlame != ThreadLocalWorkSource.UID_NONE) {
+ Binder.setCallingWorkSourceUid(uidToBlame);
+ }
+ final int nestedUid = callGetIncomingWorkSourceUid();
+ return new int[] {uid, nestedUid};
+ }
+
+ public int[] nestedCall() {
+ final int uid = Binder.getCallingWorkSourceUid();
+ final int nestedUid = callGetIncomingWorkSourceUid();
+ return new int[] {uid, nestedUid};
+ }
+
+ private int callGetIncomingWorkSourceUid() {
+ BlockingQueue<IBinderWorkSourceService> blockingQueue =
+ new LinkedBlockingQueue<>();
+ ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ blockingQueue.add(IBinderWorkSourceService.Stub.asInterface(service));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+
+ Context context = getApplicationContext();
+ context.bindService(
+ new Intent(context, BinderWorkSourceService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+
+ final IBinderWorkSourceService service;
+ try {
+ service = blockingQueue.poll(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (service == null) {
+ throw new RuntimeException("Gave up waiting for BinderWorkSourceService");
+ }
+
+ try {
+ return service.getIncomingWorkSourceUid();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ context.unbindService(mConnection);
+ }
+ }
+ };
+
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
new file mode 100644
index 0000000..ac8d7ab
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.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 android.os;
+
+import android.app.Service;
+import android.content.Intent;
+
+/**
+ * Service used by {@link BinderWorkSourceTest}.
+ */
+public class BinderWorkSourceService extends Service {
+ private final IBinderWorkSourceService.Stub mBinder =
+ new IBinderWorkSourceService.Stub() {
+ public int getIncomingWorkSourceUid() {
+ return Binder.getCallingWorkSourceUid();
+ }
+ };
+
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
new file mode 100644
index 0000000..ec17803
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test whether Binder calls work source is propagated correctly.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BinderWorkSourceTest {
+ private static Context sContext;
+ private static final int UID = 100;
+ private static final int SECOND_UID = 200;
+ private static final int UID_NONE = ThreadLocalWorkSource.UID_NONE;
+
+ private IBinderWorkSourceService mService;
+ private IBinderWorkSourceNestedService mNestedService;
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IBinderWorkSourceService.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ private ServiceConnection mNestedConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mNestedService = IBinderWorkSourceNestedService.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mNestedService = null;
+ }
+ };
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ sContext = InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ sContext.bindService(
+ new Intent(sContext, BinderWorkSourceService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ sContext.bindService(
+ new Intent(sContext, BinderWorkSourceNestedService.class),
+ mNestedConnection, Context.BIND_AUTO_CREATE);
+
+ final long timeoutMs = System.currentTimeMillis() + 30_000;
+ while ((mService == null || mNestedService == null)
+ && System.currentTimeMillis() < timeoutMs) {
+ Thread.sleep(1_000);
+ }
+ assertNotNull("Gave up waiting for BinderWorkSourceService", mService);
+ assertNotNull("Gave up waiting for BinderWorkSourceNestedService", mNestedService);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ sContext.unbindService(mConnection);
+ sContext.unbindService(mNestedConnection);
+ }
+
+ @Test
+ public void setWorkSource() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void clearWorkSource() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ Binder.clearCallingWorkSource();
+ assertEquals(UID_NONE, mService.getIncomingWorkSourceUid());
+ assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void setWorkSource_propagatedForMultipleCalls() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void restoreWorkSource() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ long token = Binder.clearCallingWorkSource();
+ Binder.restoreCallingWorkSource(token);
+
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void nestedSetWorkSoucePropagated() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+
+ int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(SECOND_UID);
+ assertEquals(UID, workSources[0]);
+ // UID set in ested call.
+ assertEquals(SECOND_UID, workSources[1]);
+ // Initial work source restored.
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void nestedSetWorkSouceDoesNotEnablePropagation() throws Exception {
+ int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(UID);
+ assertEquals(UID_NONE, workSources[0]);
+ // UID set in ested call.
+ assertEquals(UID, workSources[1]);
+ // Initial work source restored.
+ assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void nestedSetWorkSouceNotPropagated() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+
+ int[] workSources = mNestedService.nestedCall();
+ assertEquals(UID, workSources[0]);
+ // No UID propagated.
+ assertEquals(UID_NONE, workSources[1]);
+ // Initial work source restored.
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
similarity index 73%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
index c66a6cb..365aebb 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.os;
-parcelable ContentCaptureEvent;
+interface IBinderWorkSourceNestedService {
+ int[] nestedCallWithWorkSourceToSet(int uidToBlame);
+ int[] nestedCall();
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
similarity index 78%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
index c66a6cb..05d4e82 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,6 +14,8 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package android.os;
-parcelable ContentCaptureEvent;
+interface IBinderWorkSourceService {
+ int getIncomingWorkSourceUid();
+}
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index c8bc35c..9e15231 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -16,6 +16,10 @@
package android.os;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.RedactingFileDescriptor.removeRange;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -58,8 +62,8 @@
@Test
public void testSingleByte() throws Exception {
- final FileDescriptor fd = RedactingFileDescriptor
- .open(mContext, mFile, new long[] { 10, 11 }).getFileDescriptor();
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+ new long[] { 10, 11 }).getFileDescriptor();
final byte[] buf = new byte[1_000];
assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
@@ -74,8 +78,8 @@
@Test
public void testRanges() throws Exception {
- final FileDescriptor fd = RedactingFileDescriptor
- .open(mContext, mFile, new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+ new long[] { 100, 200, 300, 400 }).getFileDescriptor();
final byte[] buf = new byte[10];
assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
@@ -96,8 +100,8 @@
@Test
public void testEntireFile() throws Exception {
- final FileDescriptor fd = RedactingFileDescriptor
- .open(mContext, mFile, new long[] { 0, 5_000_000 }).getFileDescriptor();
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+ new long[] { 0, 5_000_000 }).getFileDescriptor();
try (FileInputStream in = new FileInputStream(fd)) {
int val;
@@ -106,4 +110,61 @@
}
}
}
+
+ @Test
+ public void testReadWrite() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+
+ // Redacted at first
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 0, 0, 0 }, buf);
+
+ // But we can see data that we've written
+ Os.pwrite(fd, new byte[] { 32, 32 }, 0, 2, 102);
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 32, 32, 0 }, buf);
+ }
+
+ @Test
+ public void testRemoveRange() throws Exception {
+ // Removing outside ranges should have no changes
+ assertArrayEquals(new long[] { 100, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 0, 100));
+ assertArrayEquals(new long[] { 100, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 200, 300));
+ assertArrayEquals(new long[] { 100, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 400, 500));
+
+ // Removing full regions
+ assertArrayEquals(new long[] { 100, 200 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 300, 400));
+ assertArrayEquals(new long[] { 100, 200 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 250, 450));
+ assertArrayEquals(new long[] { 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 50, 250));
+ assertArrayEquals(new long[] { },
+ removeRange(new long[] { 100, 200, 300, 400 }, 0, 5_000_000));
+ }
+
+ @Test
+ public void testRemoveRange_Partial() throws Exception {
+ assertArrayEquals(new long[] { 150, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 50, 150));
+ assertArrayEquals(new long[] { 100, 150, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 150, 250));
+ assertArrayEquals(new long[] { 100, 150, 350, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 150, 350));
+ assertArrayEquals(new long[] { 100, 150 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 150, 500));
+ }
+
+ @Test
+ public void testRemoveRange_Hole() throws Exception {
+ assertArrayEquals(new long[] { 100, 125, 175, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 125, 175));
+ assertArrayEquals(new long[] { 100, 200 },
+ removeRange(new long[] { 100, 200 }, 150, 150));
+ }
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 002b6a8..1d72a03 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -124,7 +124,9 @@
Settings.Global.AUTOFILL_LOGGING_LEVEL,
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,
Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
+ Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -181,6 +183,9 @@
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,
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
@@ -189,6 +194,10 @@
Settings.Global.DATA_ROAMING,
Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+ Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+ Settings.Global.DATA_STALL_EVALUATION_TYPE,
+ Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+ Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
Settings.Global.DEBUG_APP,
Settings.Global.DEBUG_VIEW_ATTRIBUTES,
Settings.Global.DEFAULT_DNS_SERVER,
@@ -239,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,
@@ -281,6 +291,7 @@
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
@@ -306,6 +317,7 @@
Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
Settings.Global.MULTI_SIM_VOICE_PROMPT,
+ Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
Settings.Global.NETSTATS_DEV_DELETE_AGE,
Settings.Global.NETSTATS_DEV_PERSIST_BYTES,
@@ -467,6 +479,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,
@@ -653,7 +666,10 @@
Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
Settings.Secure.FLASHLIGHT_AVAILABLE,
- Settings.Secure.FLASHLIGHT_ENABLED);
+ Settings.Secure.FLASHLIGHT_ENABLED,
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+ Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+ Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
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/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
new file mode 100644
index 0000000..0c7e730
--- /dev/null
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,d
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class AndroidCharacterTest {
+
+ @Test
+ public void testGetDirectionalities_nonSupplementaryCharacters() {
+ int size = Character.MAX_VALUE + 1
+ - (Character.MAX_SURROGATE - Character.MIN_SURROGATE + 1);
+ char[] chars = new char[size];
+ byte[] java_lang_results = new byte[size];
+ int index = 0;
+ for (int cp = 0; cp <= Character.MAX_VALUE; cp++) {
+ if (cp < Character.MIN_SURROGATE || cp > Character.MAX_SURROGATE) {
+ chars[index] = (char) cp;
+ java_lang_results[index] = Character.getDirectionality(cp);
+ index++;
+ }
+ }
+
+ byte[] android_text_results = new byte[size];
+ AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+ assertArrayEquals(java_lang_results, android_text_results);
+ }
+
+ @Test
+ public void testGetDirectionalities_supplementaryCharacters() {
+ int maxNumberOfChars = Character.MAX_CODE_POINT - Character.MIN_SUPPLEMENTARY_CODE_POINT
+ + 1;
+ int size = maxNumberOfChars * 2;
+ char[] chars = new char[size];
+ byte[] java_lang_results = new byte[size];
+ int index = 0;
+ for (int cp = Character.MIN_SUPPLEMENTARY_CODE_POINT; cp <= Character.MAX_CODE_POINT;
+ cp++) {
+ chars[index] = Character.highSurrogate(cp);
+ chars[index + 1] = Character.lowSurrogate(cp);
+ java_lang_results[index] = java_lang_results[index + 1] = Character
+ .getDirectionality(cp);
+ index += 2;
+ }
+
+ byte[] android_text_results = new byte[size];
+ AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+ assertArrayEquals(java_lang_results, android_text_results);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
new file mode 100644
index 0000000..ed80cd7
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsControllerTest {
+
+ private InsetsController mController = new InsetsController();
+
+ private SurfaceSession mSession = new SurfaceSession();
+ private SurfaceControl mLeash;
+
+ @Before
+ public void setup() {
+ mLeash = new SurfaceControl.Builder(mSession)
+ .setName("testSurface")
+ .build();
+ }
+
+ @Test
+ public void testControlsChanged() {
+ InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash);
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+ assertEquals(mLeash,
+ mController.getSourceConsumer(TYPE_TOP_BAR).getControl().getLeash());
+ }
+
+ @Test
+ public void testControlsRevoked() {
+ InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash);
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+ assertNull(mController.getSourceConsumer(TYPE_TOP_BAR).getControl());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
new file mode 100644
index 0000000..5a20ba2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.view.InsetsState.TYPE_TOP_BAR;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl.Transaction;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceConsumerTest {
+
+ private InsetsSourceConsumer mConsumer;
+
+ private SurfaceSession mSession = new SurfaceSession();
+ private SurfaceControl mLeash;
+ @Mock Transaction mMockTransaction;
+ @Mock InsetsController mMockController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mLeash = new SurfaceControl.Builder(mSession)
+ .setName("testSurface")
+ .build();
+ mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(),
+ () -> mMockTransaction);
+ mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
+ }
+
+ @Test
+ public void testHide() {
+ mConsumer.hide();
+ verify(mMockTransaction).hide(eq(mLeash));
+ }
+
+ @Test
+ public void testShow() {
+ mConsumer.hide();
+ mConsumer.show();
+ verify(mMockTransaction, atLeastOnce()).show(eq(mLeash));
+ }
+
+ @Test
+ public void testRestore() {
+ mConsumer.setControl(null);
+ reset(mMockTransaction);
+ mConsumer.hide();
+ verifyZeroInteractions(mMockTransaction);
+ mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
+ verify(mMockTransaction).hide(eq(mLeash));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
new file mode 100644
index 0000000..ed472d2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceTest {
+
+ private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR);
+
+ @Before
+ public void setUp() {
+ mSource.setVisible(true);
+ }
+
+ @Test
+ public void testCalculateInsetsTop() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsetsBottom() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void testCalculateInsetsLeft() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsetsRight() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_overextend() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_invisible() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_ignoreVisibility() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+ true /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ // Parcel and equals already tested via InsetsStateTest
+}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
new file mode 100644
index 0000000..6bb9539
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.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.view;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsStateTest {
+
+ private InsetsState mState = new InsetsState();
+ private InsetsState mState2 = new InsetsState();
+
+ @Test
+ public void testCalculateInsets() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+ }
+
+ @Test
+ public void testCalculateInsets_imeAndNav() {
+ mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(100, insets.getStableInsetBottom());
+ assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+ }
+
+ @Test
+ public void testCalculateInsets_navRightStatusTop() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+ }
+
+ @Test
+ public void testStripForDispatch() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ mState.removeSource(TYPE_IME);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(0, insets.getSystemWindowInsetBottom());
+ }
+
+ @Test
+ public void testEquals_differentRect() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10));
+ assertNotEquals(mState, mState2);
+ }
+
+ @Test
+ public void testEquals_differentSource() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ assertNotEquals(mState, mState2);
+ }
+
+ @Test
+ public void testEquals_sameButDifferentInsertOrder() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ assertEquals(mState, mState2);
+ }
+
+ @Test
+ public void testEquals_visibility() {
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_IME).setVisible(true);
+ mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ assertNotEquals(mState, mState2);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_IME).setVisible(true);
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ Parcel p = Parcel.obtain();
+ mState.writeToParcel(p, 0 /* flags */);
+ mState2.readFromParcel(p);
+ p.recycle();
+ assertEquals(mState, mState2);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
new file mode 100644
index 0000000..f0faaf6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActionsSuggestionsHelperTest {
+ @Test
+ public void testToNativeMessages_emptyInput() {
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+
+ assertThat(conversationMessages).isEmpty();
+ }
+
+ @Test
+ public void testToNativeMessages_noTextMessages() {
+ ConversationActions.Message messageWithoutText =
+ new ConversationActions.Message.Builder().build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Collections.singletonList(messageWithoutText));
+
+ 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()
+ .setText("first")
+ .setAuthor(userB)
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .setAuthor(userA)
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+ .build();
+ ConversationActions.Message fourthMessage =
+ new ConversationActions.Message.Builder()
+ .setText("fourth")
+ .setAuthor(userA)
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+ 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);
+ }
+
+ private static void assertNativeMessage(
+ ActionsSuggestionsModel.ConversationMessage nativeMessage,
+ CharSequence text,
+ int userId) {
+ assertThat(nativeMessage.getText()).isEqualTo(text.toString());
+ assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
new file mode 100644
index 0000000..0180856
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A builder used to build a fake context for testing.
+ */
+// TODO: Consider making public.
+final class FakeContextBuilder {
+
+ /**
+ * A component name that can be used for tests.
+ */
+ public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls");
+
+ private final PackageManager mPackageManager;
+ private final ContextWrapper mContext;
+ private final Map<String, ComponentName> mComponents = new HashMap<>();
+ private @Nullable ComponentName mAllIntentComponent;
+
+ FakeContextBuilder() {
+ mPackageManager = mock(PackageManager.class);
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
+ mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+ };
+ }
+
+ /**
+ * Sets the component name of an activity to handle the specified intent action.
+ * <p>
+ * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+ */
+ public FakeContextBuilder setIntentComponent(
+ String intentAction, @Nullable ComponentName component) {
+ Preconditions.checkNotNull(intentAction);
+ mComponents.put(intentAction, component);
+ return this;
+ }
+
+
+ /**
+ * Sets the component name of an activity to handle all intents.
+ * <p>
+ * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+ */
+ public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) {
+ mAllIntentComponent = component;
+ return this;
+ }
+
+ /**
+ * Builds and returns a fake context.
+ */
+ public Context build() {
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenAnswer(
+ (Answer<ResolveInfo>) invocation -> {
+ final String action = ((Intent) invocation.getArgument(0)).getAction();
+ final ComponentName component = mComponents.containsKey(action)
+ ? mComponents.get(action)
+ : mAllIntentComponent;
+ return getResolveInfo(component);
+ });
+ return mContext;
+ }
+
+ /**
+ * Returns a component name with random package and class names.
+ */
+ public static ComponentName newComponent() {
+ return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+ }
+
+ private static ResolveInfo getResolveInfo(ComponentName component) {
+ final ResolveInfo info;
+ if (component == null) {
+ info = null;
+ } else {
+ // NOTE: If something breaks in TextClassifier because we expect more fields to be set
+ // in here, just add them.
+ info = new ResolveInfo();
+ info.activityInfo = new ActivityInfo();
+ info.activityInfo.packageName = component.getPackageName();
+ info.activityInfo.name = component.getClassName();
+ info.activityInfo.exported = true;
+ info.activityInfo.applicationInfo = new ApplicationInfo();
+ info.activityInfo.applicationInfo.icon = 0;
+ }
+ return info;
+ }
+
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
new file mode 100644
index 0000000..bae2be3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.AnnotatorModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentFactoryTest {
+
+ private static final String TEXT = "text";
+
+ @Test
+ public void create_typeDictionary() {
+ AnnotatorModel.ClassificationResult classificationResult =
+ new AnnotatorModel.ClassificationResult(
+ TextClassifier.TYPE_DICTIONARY,
+ 1.0f,
+ null,
+ null);
+
+ List<TextClassifierImpl.LabeledIntent> intents = TextClassifierImpl.IntentFactory.create(
+ InstrumentationRegistry.getContext(),
+ TEXT,
+ false,
+ null,
+ classificationResult);
+
+ assertThat(intents).hasSize(1);
+ TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
+ Intent intent = labeledIntent.getIntent();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+ assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 46aa5b4..a3f69d9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -20,18 +20,10 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.LocaleList;
import android.service.textclassifier.TextClassifierService;
import android.support.test.InstrumentationRegistry;
@@ -41,7 +33,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -78,23 +69,10 @@
@Test
public void testCannotResolveIntent() {
- final PackageManager fakePackageMgr = mock(PackageManager.class);
-
- ResolveInfo validInfo = mContext.getPackageManager().resolveActivity(
- new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0);
- // Make packageManager fail when it gets the following intent:
- ArgumentMatcher<Intent> toFailIntent =
- intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT);
-
- when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo);
- when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null);
-
- ContextWrapper fakeContext = new ContextWrapper(mContext) {
- @Override
- public PackageManager getPackageManager() {
- return fakePackageMgr;
- }
- };
+ Context fakeContext = new FakeContextBuilder()
+ .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
+ .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
+ .build();
TextClassifier fallback = TextClassifier.NO_OP;
TextClassifier classifier = new TextClassifierImpl(
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 06ba15e..aec4571 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -23,9 +23,12 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.content.Intent;
import android.os.LocaleList;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -52,12 +55,18 @@
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object> textClassifierTypes() {
- return Arrays.asList(LOCAL, SYSTEM);
+ return Arrays.asList(LOCAL);
+
+ // TODO: The following will fail on any device that specifies a no-op TextClassifierService.
+ // Enable when we can set a specified TextClassifierService for testing.
+ // return Arrays.asList(LOCAL, SYSTEM);
}
@Parameterized.Parameter
public String mTextClassifierType;
+ private static final TextClassificationConstants TC_CONSTANTS =
+ TextClassificationConstants.loadFromString("");
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
private static final String NO_TYPE = null;
@@ -74,7 +83,7 @@
}
@Test
- public void testSmartSelection() {
+ public void testSuggestSelection() {
if (isTextClassifierDisabled()) return;
String text = "Contact me at droid@android.com";
@@ -95,7 +104,7 @@
}
@Test
- public void testSmartSelection_url() {
+ public void testSuggestSelection_url() {
if (isTextClassifierDisabled()) return;
String text = "Visit http://www.android.com for more information";
@@ -151,7 +160,7 @@
}
@Test
- public void testTextClassifyText_url() {
+ public void testClassifyText_url() {
if (isTextClassifierDisabled()) return;
String text = "Visit www.android.com for more information";
@@ -168,7 +177,7 @@
}
@Test
- public void testTextClassifyText_address() {
+ public void testClassifyText_address() {
if (isTextClassifierDisabled()) return;
String text = "Brandschenkestrasse 110, Zürich, Switzerland";
@@ -182,7 +191,7 @@
}
@Test
- public void testTextClassifyText_url_inCaps() {
+ public void testClassifyText_url_inCaps() {
if (isTextClassifierDisabled()) return;
String text = "Visit HTTP://ANDROID.COM for more information";
@@ -199,7 +208,7 @@
}
@Test
- public void testTextClassifyText_date() {
+ public void testClassifyText_date() {
if (isTextClassifierDisabled()) return;
String text = "Let's meet on January 9, 2018.";
@@ -216,7 +225,7 @@
}
@Test
- public void testTextClassifyText_datetime() {
+ public void testClassifyText_datetime() {
if (isTextClassifierDisabled()) return;
String text = "Let's meet 2018/01/01 10:30:20.";
@@ -234,6 +243,30 @@
}
@Test
+ public void testClassifyText_foreignText() {
+ LocaleList originalLocales = LocaleList.getDefault();
+ LocaleList.setDefault(LocaleList.forLanguageTags("en"));
+ String foreignText = "これは日本語のテキストです";
+
+ Context context = new FakeContextBuilder()
+ .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
+ .build();
+ TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ foreignText, 0, foreignText.length())
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = classifier.classifyText(request);
+ assertEquals(1, classification.getActions().size());
+ assertEquals(
+ context.getString(com.android.internal.R.string.translate),
+ classification.getActions().get(0).getTitle());
+
+ LocaleList.setDefault(originalLocales);
+ }
+
+ @Test
public void testGenerateLinks_phone() {
if (isTextClassifierDisabled()) return;
String text = "The number is +12122537077. See you tonight!";
@@ -296,6 +329,17 @@
assertTrue(links.getLinks().isEmpty());
}
+ @Test
+ public void testApplyLinks_unsupportedCharacter() {
+ if (isTextClassifierDisabled()) return;
+ Spannable url = new SpannableString("\u202Emoc.diordna.com");
+ TextLinks.Request request = new TextLinks.Request.Builder(url).build();
+ assertEquals(
+ TextLinks.STATUS_UNSUPPORTED_CHARACTER,
+ mClassifier.generateLinks(request).apply(url, 0, null));
+ }
+
+
@Test(expected = IllegalArgumentException.class)
public void testGenerateLinks_tooLong() {
if (isTextClassifierDisabled()) {
@@ -329,7 +373,7 @@
public void testSuggestConversationActions_textReplyOnly_maxThree() {
if (isTextClassifierDisabled()) return;
ConversationActions.Message message =
- new ConversationActions.Message.Builder().setText("Hello").build();
+ new ConversationActions.Message.Builder().setText("Where are you?").build();
ConversationActions.TypeConfig typeConfig =
new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
.setIncludedTypes(
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 4456122..36792bb 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -467,6 +467,21 @@
assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"});
}
+ @Test
+ public void setIntTag() {
+ RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ int index = 10;
+ views.setIntTag(
+ R.id.layout, com.android.internal.R.id.notification_action_index_tag, index);
+
+ RemoteViews recovered = parcelAndRecreate(views);
+ RemoteViews cloned = new RemoteViews(recovered);
+ View inflated = cloned.apply(mContext, mContainer);
+
+ assertEquals(
+ index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
+ }
+
private class WidgetContainer extends AppWidgetHostView {
int[] mSharedViewIds;
String[] mSharedViewNames;
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 70dc618..90758ba 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -983,6 +983,19 @@
}
@Test
+ public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
+ useSystemDefaultTextClassifier();
+ final String text = "\u202Emoc.diordna.com";
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ mActivityRule.runOnUiThread(() -> textView.setText(text));
+ mInstrumentation.waitForIdleSync();
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+ }
+
+ @Test
public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable {
final List<SelectionEvent> selectionEvents = new ArrayList<>();
final TextClassifier classifier = new TextClassifier() {
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 9fcb06e..404c99c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -103,6 +103,49 @@
}
@Test
+ public void setMaxHeight() throws Exception {
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ waitForIdle();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ final View resolverList = activity.findViewById(R.id.resolver_list);
+ final int initialResolverHeight = resolverList.getHeight();
+
+ activity.runOnUiThread(() -> {
+ ResolverDrawerLayout layout = (ResolverDrawerLayout)
+ activity.findViewById(
+ R.id.contentPanel);
+ ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+ = initialResolverHeight - 1;
+ // Force a relayout
+ layout.invalidate();
+ layout.requestLayout();
+ });
+ waitForIdle();
+ assertThat("Drawer should be capped at maxHeight",
+ resolverList.getHeight() == (initialResolverHeight - 1));
+
+ activity.runOnUiThread(() -> {
+ ResolverDrawerLayout layout = (ResolverDrawerLayout)
+ activity.findViewById(
+ R.id.contentPanel);
+ ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+ = initialResolverHeight + 1;
+ // Force a relayout
+ layout.invalidate();
+ layout.requestLayout();
+ });
+ waitForIdle();
+ assertThat("Drawer should not change height if its height is less than maxHeight",
+ resolverList.getHeight() == initialResolverHeight);
+ }
+
+ @Test
public void setShowAtTopToTrue() throws Exception {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 3cfc644..225515e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,6 +39,10 @@
BatteryStatsUserLifecycleTests.class,
KernelCpuProcReaderTest.class,
KernelCpuProcStringReaderTest.class,
+ KernelCpuUidActiveTimeReaderTest.class,
+ KernelCpuUidClusterTimeReaderTest.class,
+ KernelCpuUidFreqTimeReaderTest.class,
+ KernelCpuUidUserSysTimeReaderTest.class,
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
KernelUidCpuFreqTimeReaderTest.class,
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 30309cf..e261819 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;
@@ -387,8 +388,7 @@
@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);
@@ -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();
@@ -557,6 +541,83 @@
assertEquals(0, bcs.getExceptionCounts().size());
}
+ @Test
+ public void testOverflow_sameEntry() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(2);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+ assertEquals(1, callStatsList.size());
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(3, callStats.callCount);
+ }
+
+ @Test
+ public void testOverflow_overflowEntry() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(1);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 2);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
+ assertEquals(2, callStatsList.size());
+ BinderCallsStats.ExportedCallStat callStats = callStatsList.get(0);
+ assertEquals(1, callStats.callCount);
+ assertEquals("1", callStats.methodName);
+ assertEquals("android.os.Binder", callStats.className);
+ assertEquals(CALLING_UID, callStats.callingUid);
+
+ callStats = callStatsList.get(1);
+ assertEquals(1, callStats.callCount);
+ assertEquals("-1", callStats.methodName);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
+ assertEquals(CALLING_UID, callStats.callingUid);
+ }
+
+ @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;
@@ -564,6 +625,10 @@
public long elapsedTime = 0;
TestBinderCallsStats() {
+ this(mDeviceState);
+ }
+
+ TestBinderCallsStats(CachedDeviceState deviceState) {
// Make random generator not random.
super(new Injector() {
public Random getRandomGenerator() {
@@ -577,7 +642,10 @@
}
});
setSamplingInterval(1);
- setDeviceState(mDeviceState.getReadonlyClient());
+ setAddDebugEntries(false);
+ if (deviceState != null) {
+ setDeviceState(deviceState.getReadonlyClient());
+ }
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index dae9eb5..2663f2b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -37,6 +37,7 @@
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.nio.CharBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
@@ -149,7 +150,7 @@
+ "0 0 1 1 1 0 2 0 221",
iter.nextLine().toString());
long[] actual = new long[43];
- iter.nextLineAsArray(actual);
+ KernelCpuProcStringReader.asLongs(iter.nextLine(), actual);
assertArrayEquals(
new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
@@ -183,7 +184,7 @@
}
}
- /** Tests nextLineToArray functionality. */
+ /** Tests reading lines, then converting to long[]. */
@Test
public void testReadLineToArray() throws Exception {
final long[][] data = getTestArray(800, 50);
@@ -193,12 +194,32 @@
long[] actual = new long[50];
try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
for (long[] expected : data) {
- assertEquals(50, iter.nextLineAsArray(actual));
+ CharBuffer cb = iter.nextLine();
+ String before = cb.toString();
+ assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual));
assertArrayEquals(expected, actual);
+ assertEquals("Buffer not reset to the pos before reading", before, cb.toString());
}
}
}
+ /** Tests error handling of converting to long[]. */
+ @Test
+ public void testLineToArrayErrorHandling() {
+ long[] actual = new long[100];
+ String invalidChar = "123: -1234 456";
+ String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123";
+ CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length());
+ assertEquals("Failed to report err for: " + invalidChar, -2,
+ KernelCpuProcStringReader.asLongs(cb, actual));
+ assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString());
+
+ cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length());
+ assertEquals("Failed to report err for: " + overflow, -3,
+ KernelCpuProcStringReader.asLongs(cb, actual));
+ assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString());
+ }
+
/**
* Tests that reading a file over the limit (1MB) will return null.
*/
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
new file mode 100644
index 0000000..adafda0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidActiveTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidActiveTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidActiveTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int mCpus = 4;
+ private final String mHeadline = "cpus: 4\n";
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidActiveTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] newTimes1 = increaseTime(times);
+ writeToFile(mHeadline + uidLines(mUids, newTimes1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] newTimes2 = increaseTime(newTimes1);
+ writeToFile(mHeadline + uidLines(mUids, newTimes2));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] newTimes3 = increaseTime(newTimes2);
+ writeToFile(mHeadline + uidLines(mUids, newTimes3));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaDecreasedTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+ times2[0][0] = 100;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testReadDeltaNegativeTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time is -ve.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] *= -1;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long getActiveTime(long[] times) {
+ return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> {
+ SparseLongArray mData = new SparseLongArray();
+
+ public void verify(int uid, long time) {
+ assertEquals(time, mData.get(uid));
+ mData.delete(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, Long time) {
+ mData.put(uid, time);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
new file mode 100644
index 0000000..ad20d84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidClusterTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidClusterTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidClusterTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int mCpus = 6;
+ private final String mHeadline = "policy0: 4 policy4: 2\n";
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidClusterTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], clusterTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] times4 = increaseTime(times3);
+ writeToFile(mHeadline + uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], clusterTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], clusterTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaDecreasedTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+ times2[0][0] = 100;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testReadDeltaNegativeTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] *= -1;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ private long[] clusterTime(long[] times) {
+ // Assumes 4 + 2 cores
+ return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
+ times[4] + times[5] / 2};
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
new file mode 100644
index 0000000..1d3a98a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+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.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidFreqTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidFreqTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidFreqTimeReader mReader;
+ private VerifiableCallback mCallback;
+ @Mock
+ private PowerProfile mPowerProfile;
+
+ private Random mRand = new Random(12345);
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
+ final long[][] freqs = {
+ {1, 12, 123, 1234},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
+ {1, 12, 123, 23, 2345, 234567}
+ };
+ final int[] numClusters = {2, 2, 3, 1};
+ final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
+ for (int i = 0; i < freqs.length; ++i) {
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+ writeToFile(freqsLine(freqs[i]));
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+ Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+ assertFalse(errMsg, mReader.perClusterTimesAvailable());
+
+ // Verify that a second call won't read the proc file again
+ assertTrue(mTestFile.delete());
+ actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ assertFalse(errMsg, mReader.perClusterTimesAvailable());
+ }
+ }
+
+ @Test
+ public void testReadFreqs_perClusterTimesAvailable() throws Exception {
+ final long[][] freqs = {
+ {1, 12, 123, 1234},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
+ };
+ final int[] numClusters = {1, 2, 3};
+ final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
+ for (int i = 0; i < freqs.length; ++i) {
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+ writeToFile(freqsLine(freqs[i]));
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+ Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+ assertTrue(errMsg, mReader.perClusterTimesAvailable());
+
+ // Verify that a second call won't read the proc file again
+ assertTrue(mTestFile.delete());
+ actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ assertTrue(errMsg, mReader.perClusterTimesAvailable());
+ }
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], times[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that readDelta also reads the frequencies if not already available.
+ assertTrue(mTestFile.delete());
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs, actualFreqs);
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] newTimes1 = increaseTime(times);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] newTimes2 = increaseTime(newTimes1);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] newTimes3 = increaseTime(newTimes2);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that readDelta also reads the frequencies if not already available.
+ assertTrue(mTestFile.delete());
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs, actualFreqs);
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times2[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaWrongData() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] = 1000;
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times3[i], times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ mCallback.clear();
+ final long[][] times4 = increaseTime(times3);
+ times4[0][0] *= -1;
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ mCallback.clear();
+ final long[][] times5 = increaseTime(times4);
+ times5[0] = increaseTime(times3)[0];
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
+ }
+
+ assertTrue(mTestFile.delete());
+ }
+
+ private String freqsLine(long[] freqs) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("uid:");
+ for (int i = 0; i < freqs.length; ++i) {
+ sb.append(" " + freqs[i]);
+ }
+ return sb.append('\n').toString();
+ }
+
+ private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
+ assertEquals(numClusters, clusterFreqs.length);
+ when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
+ for (int i = 0; i < numClusters; ++i) {
+ when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
+ }
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 10 + 10;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
new file mode 100644
index 0000000..9b4512b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidUserSysTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidUserSysTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidUserSysTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+ private final long[][] mInitialTimes = new long[][]{
+ {15334000, 310964000},
+ {537000, 114000},
+ {40000, 10000},
+ {170000, 57000},
+ {5377000, 867000},
+ {47000, 17000}
+ };
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidUserSysTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testThrottler() throws Exception {
+ mReader = new KernelCpuUidUserSysTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
+ mReader.setThrottle(500);
+
+ writeToFile(uidLines(mUids, mInitialTimes));
+ mReader.readDelta(mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ long[][] times1 = increaseTime(mInitialTimes);
+ writeToFile(uidLines(mUids, times1));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(0, mCallback.mData.size());
+
+ SystemClock.sleep(600);
+
+ long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ long[][] times3 = increaseTime(times2);
+ writeToFile(uidLines(mUids, times3));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(0, mCallback.mData.size());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that a second call will only return deltas.
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ final long[][] times3 = increaseTime(times2);
+ writeToFile(uidLines(mUids, times3));
+ mReader.readDelta(null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ final long[][] times4 = increaseTime(times3);
+ writeToFile(uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaWrongData() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] = 1000;
+ writeToFile(uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that a second call should still return absolute values
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times2[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j]);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(1000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
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 f637b7c..f26dfad 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -105,7 +105,6 @@
assertThat(entry.recordedDelayMessageCount).isEqualTo(1);
assertThat(entry.delayMillis).isEqualTo(30);
assertThat(entry.maxDelayMillis).isEqualTo(30);
-
}
@Test
@@ -274,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));
@@ -429,6 +427,32 @@
assertThat(entries).hasSize(0);
}
+ @Test
+ public void testAddsDebugEntries() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ looperStats.setAddDebugEntries(true);
+
+ Message message = mHandlerFirst.obtainMessage(1000);
+ message.when = looperStats.getSystemUptimeMillis();
+ Object token = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token, message);
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(4);
+ LooperStats.ExportedEntry debugEntry1 = entries.get(1);
+ assertThat(debugEntry1.handlerClassName).isEqualTo("");
+ assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
+ 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.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) {
try {
r.run();
@@ -447,9 +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());
+ 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 a4c5ed2..141948f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -201,6 +201,24 @@
<new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</split-permission>
+ <!-- Apps holding either the legacy READ or WRITE permissions will inherit
+ the ability to <em>read</em> new typed permissions in the Q release; they
+ won't gain the ability to <em>write</em> that content. -->
+ <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="10000">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+ </split-permission>
+ <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+ <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"
+ targetSdk="10000">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+ </split-permission>
+
<!-- This is a list of all the libraries available for application
code to link against. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9e4ea32..f237344 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -281,6 +281,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 +409,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/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3b0dc9d..135c137 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -64,7 +64,7 @@
public boolean isRecordingFor(Object o) { return false; }
// may be null
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521088)
private Bitmap mBitmap;
// optional field set by the caller
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2e1d81a..2227cf5 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1779,6 +1779,70 @@
}
/**
+ * <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>
+ *
+ * <p>The transform is computed using the von Kries method, described
+ * in more details in the documentation of {@link Adaptation}. The
+ * {@link Adaptation} enum provides different matrices that can be
+ * used to perform the adaptation.</p>
+ *
+ * @param adaptation The adaptation method
+ * @param srcWhitePoint The white point to adapt from
+ * @param dstWhitePoint The white point to adapt to
+ * @return A 3x3 matrix as a non-null array of 9 floats
+ */
+ @NonNull
+ @Size(9)
+ public static float[] chromaticAdaptation(@NonNull Adaptation adaptation,
+ @NonNull @Size(min = 2, max = 3) float[] srcWhitePoint,
+ @NonNull @Size(min = 2, max = 3) float[] dstWhitePoint) {
+ float[] srcXyz = srcWhitePoint.length == 3 ?
+ Arrays.copyOf(srcWhitePoint, 3) : xyYToXyz(srcWhitePoint);
+ float[] dstXyz = dstWhitePoint.length == 3 ?
+ Arrays.copyOf(dstWhitePoint, 3) : xyYToXyz(dstWhitePoint);
+
+ if (compare(srcXyz, dstXyz)) {
+ return new float[] {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+ };
+ }
+ return chromaticAdaptation(adaptation.mTransform, srcXyz, dstXyz);
+ }
+
+ /**
* Implementation of the CIE XYZ color space. Assumes the white point is D50.
*/
@AnyThread
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index de110c8..d9da27c 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -82,6 +82,17 @@
}
/**
+ * Add two Insets.
+ *
+ * @param a The first Insets to add.
+ * @param b The second Insets to add.
+ * @return a + b, i. e. all insets on every side are added together.
+ */
+ public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) {
+ return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom);
+ }
+
+ /**
* Two Insets instances are equal iff they belong to the same class and their fields are
* pairwise equal.
*
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index c4dc0ad..40a32f3 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -106,6 +106,20 @@
}
/**
+ * @hide
+ */
+ public Rect(@Nullable Insets r) {
+ if (r == null) {
+ left = top = right = bottom = 0;
+ } else {
+ left = r.left;
+ top = r.top;
+ right = r.right;
+ bottom = r.bottom;
+ }
+ }
+
+ /**
* Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
*
* @hide
@@ -418,6 +432,18 @@
}
/**
+ * Insets the rectangle on all sides specified by the dimensions of {@code insets}.
+ * @hide
+ * @param insets The insets to inset the rect by.
+ */
+ public void inset(Insets insets) {
+ left += insets.left;
+ top += insets.top;
+ right -= insets.right;
+ bottom -= insets.bottom;
+ }
+
+ /**
* Insets the rectangle on all sides specified by the insets.
* @hide
* @param left The amount to add from the rectangle's left
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 45d7a21..d6f08b9 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -1173,6 +1173,22 @@
return nGetAllowForceDark(mNativeRenderNode);
}
+ /**
+ * Returns the unique ID that identifies this RenderNode. This ID is unique for the
+ * lifetime of the process. IDs are reset on process death, and are unique only within
+ * the process.
+ *
+ * This ID is intended to be used with debugging tools to associate a particular
+ * RenderNode across different debug dumping & inspection tools. For example
+ * a View layout inspector should include the unique ID for any RenderNodes that it owns
+ * to associate the drawing content with the layout content.
+ *
+ * @return the unique ID for this RenderNode
+ */
+ public long getUniqueId() {
+ return nGetUniqueId(mNativeRenderNode);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Animations
///////////////////////////////////////////////////////////////////////////
@@ -1479,4 +1495,7 @@
@CriticalNative
private static native boolean nGetAllowForceDark(long renderNode);
+
+ @CriticalNative
+ private static native long nGetUniqueId(long renderNode);
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index bf969ef..9b86b77 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -163,9 +163,6 @@
private int[] mSupportedAxes;
private static final int[] EMPTY_AXES = {};
- // The underlying font families.
- private final FontFamily[] mFamilies;
-
@UnsupportedAppUsage
private static void setDefault(Typeface t) {
sDefaultTypeface = t;
@@ -252,7 +249,22 @@
if (familyBuilder == null) {
return Typeface.DEFAULT;
}
- typeface = new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+ final FontFamily family = familyBuilder.build();
+ final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
+ FontStyle.FONT_SLANT_UPRIGHT);
+ Font bestFont = family.getFont(0);
+ int bestScore = normal.getMatchScore(bestFont.getStyle());
+ for (int i = 1; i < family.getSize(); ++i) {
+ final Font candidate = family.getFont(i);
+ final int score = normal.getMatchScore(candidate.getStyle());
+ if (score < bestScore) {
+ bestFont = candidate;
+ bestScore = score;
+ }
+ }
+ typeface = new Typeface.CustomFallbackBuilder(family)
+ .setStyle(bestFont.getStyle())
+ .build();
} catch (IOException e) {
typeface = Typeface.DEFAULT;
}
@@ -652,6 +664,18 @@
private @Nullable FontStyle mStyle;
/**
+ * Returns the maximum capacity of custom fallback families.
+ *
+ * This includes the the first font family passed to the constructor.
+ * It is guaranteed that the value will be greater than or equal to 64.
+ *
+ * @return the maximum number of font families for the custom fallback
+ */
+ public static @IntRange(from = 64) int getMaxCustomFallbackCount() {
+ return MAX_CUSTOM_FALLBACK;
+ }
+
+ /**
* Constructs a builder with a font family.
*
* @param family a family object
@@ -706,8 +730,8 @@
*/
public CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
Preconditions.checkNotNull(family);
- Preconditions.checkArgument(mFamilies.size() < MAX_CUSTOM_FALLBACK,
- "Custom fallback limit exceeded(" + MAX_CUSTOM_FALLBACK + ")");
+ Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
+ "Custom fallback limit exceeded(" + getMaxCustomFallbackCount() + ")");
mFamilies.add(family);
return this;
}
@@ -720,21 +744,17 @@
public Typeface build() {
final int userFallbackSize = mFamilies.size();
final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
- final FontFamily[] fullFamilies = new FontFamily[fallback.length + userFallbackSize];
final long[] ptrArray = new long[fallback.length + userFallbackSize];
for (int i = 0; i < userFallbackSize; ++i) {
ptrArray[i] = mFamilies.get(i).getNativePtr();
- fullFamilies[i] = mFamilies.get(i);
}
for (int i = 0; i < fallback.length; ++i) {
ptrArray[i + userFallbackSize] = fallback[i].getNativePtr();
- fullFamilies[i + userFallbackSize] = fallback[i];
}
final int weight = mStyle == null ? 400 : mStyle.getWeight();
final int italic =
(mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1;
-
- return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies);
+ return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
}
}
@@ -799,7 +819,7 @@
}
}
- typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies);
+ typeface = new Typeface(nativeCreateFromTypeface(ni, style));
styles.put(style, typeface);
}
return typeface;
@@ -867,8 +887,7 @@
}
typeface = new Typeface(
- nativeCreateFromTypefaceWithExactStyle(
- base.native_instance, weight, italic), base.mFamilies);
+ nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic));
innerCache.put(key, typeface);
}
return typeface;
@@ -878,8 +897,7 @@
public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
@NonNull List<FontVariationAxis> axes) {
final Typeface base = family == null ? Typeface.DEFAULT : family;
- return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
- base.mFamilies);
+ return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes));
}
/**
@@ -985,7 +1003,7 @@
ptrArray[i] = families[i].getNativePtr();
}
return new Typeface(nativeCreateFromArray(ptrArray,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families);
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
}
/**
@@ -1033,19 +1051,6 @@
}
native_instance = ni;
- mFamilies = new FontFamily[0];
- sRegistry.registerNativeAllocation(this, native_instance);
- mStyle = nativeGetStyle(ni);
- mWeight = nativeGetWeight(ni);
- }
-
- private Typeface(long ni, @NonNull FontFamily[] families) {
- if (ni == 0) {
- throw new IllegalStateException("native typeface cannot be made");
- }
-
- native_instance = ni;
- mFamilies = families;
sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
@@ -1072,8 +1077,7 @@
final Typeface base = systemFontMap.get(alias.getToName());
final int weight = alias.getWeight();
final Typeface newFace = weight == 400 ? base :
- new Typeface(nativeCreateWeightAlias(base.native_instance, weight),
- base.mFamilies);
+ new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
systemFontMap.put(alias.getName(), newFace);
}
}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index fdd638a..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;
@@ -108,11 +109,10 @@
* Scaled mask based on the view bounds.
*/
private final Path mMask;
+ private final Path mMaskScaleOnly;
private final Matrix mMaskMatrix;
private final Region mTransparentRegion;
- private Bitmap mMaskBitmap;
-
/**
* Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
* background layer.
@@ -151,13 +151,16 @@
*/
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));
- }
- mMask = 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();
mCanvas = new Canvas();
mTransparentRegion = new Region();
@@ -329,24 +332,19 @@
}
private void updateMaskBoundsInternal(Rect b) {
+ // reset everything that depends on the view bounds
mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+ sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+ mMaskMatrix.postTranslate(b.left, b.top);
sMask.transform(mMaskMatrix, mMask);
- if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() ||
- mMaskBitmap.getHeight() != b.height()) {
- mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+ if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+ || mLayersBitmap.getHeight() != b.height()) {
mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
}
- // mMaskBitmap bound [0, w] x [0, h]
- mCanvas.setBitmap(mMaskBitmap);
- mPaint.setShader(null);
- mCanvas.drawPath(mMask, mPaint);
- // mMask bound [left, top, right, bottom]
- mMaskMatrix.postTranslate(b.left, b.top);
- mMask.reset();
- sMask.transform(mMaskMatrix, mMask);
- // reset everything that depends on the view bounds
+ mPaint.setShader(null);
mTransparentRegion.setEmpty();
mLayersShader = null;
}
@@ -371,9 +369,11 @@
mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint.setShader(mLayersShader);
}
- if (mMaskBitmap != null) {
+ if (mMaskScaleOnly != null) {
Rect bounds = getBounds();
- canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint);
+ canvas.translate(bounds.left, bounds.top);
+ canvas.drawPath(mMaskScaleOnly, mPaint);
+ canvas.translate(-bounds.left, -bounds.top);
}
}
@@ -549,7 +549,7 @@
final ChildDrawable[] layers = mLayerState.mChildren;
for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
- if (layers[i].mDrawable.isProjected()) {
+ if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) {
return true;
}
}
@@ -674,7 +674,7 @@
@Override
public int getAlpha() {
- return PixelFormat.TRANSLUCENT;
+ return mPaint.getAlpha();
}
@Override
@@ -718,10 +718,7 @@
@Override
public int getOpacity() {
- if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
- return mLayerState.mOpacityOverride;
- }
- return mLayerState.getOpacity();
+ return PixelFormat.TRANSLUCENT;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index caf610b..5bd59d4 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -713,11 +713,12 @@
}
/**
- * Whether this drawable requests projection.
+ * Whether this drawable requests projection. Indicates that the
+ * {@link android.graphics.RenderNode} this Drawable will draw into should be drawn immediately
+ * after the closest ancestor RenderNode containing a projection receiver.
*
- * @hide magic!
+ * @see android.graphics.RenderNode#setProjectBackwards(boolean)
*/
- @UnsupportedAppUsage
public boolean isProjected() {
return false;
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 8740234..991847a 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1571,15 +1571,32 @@
st.mGradient = a.getInt(
R.styleable.GradientDrawableGradient_type, st.mGradient);
- // TODO: Update these to be themeable.
+ final boolean hasGradientColors = st.mGradientColors != null;
+ final boolean hasGradientCenter = st.hasCenterColor();
+ final int prevStart = hasGradientColors ? st.mGradientColors[0] : 0;
+ final int prevCenter = hasGradientCenter ? st.mGradientColors[1] : 0;
+ final int prevEnd;
+
+ if (st.hasCenterColor()) {
+ // if there is a center color, the end color is the last of the 3 values
+ prevEnd = st.mGradientColors[2];
+ } else if (hasGradientColors) {
+ // if there is not a center color but there are already colors configured, then
+ // the end color is the 2nd value in the array
+ prevEnd = st.mGradientColors[1];
+ } else {
+ // otherwise, there isn't a previously configured end color
+ prevEnd = 0;
+ }
+
final int startColor = a.getColor(
- R.styleable.GradientDrawableGradient_startColor, 0);
+ R.styleable.GradientDrawableGradient_startColor, prevStart);
final boolean hasCenterColor = a.hasValue(
- R.styleable.GradientDrawableGradient_centerColor);
+ R.styleable.GradientDrawableGradient_centerColor) || hasGradientCenter;
final int centerColor = a.getColor(
- R.styleable.GradientDrawableGradient_centerColor, 0);
+ R.styleable.GradientDrawableGradient_centerColor, prevCenter);
final int endColor = a.getColor(
- R.styleable.GradientDrawableGradient_endColor, 0);
+ R.styleable.GradientDrawableGradient_endColor, prevEnd);
if (hasCenterColor) {
st.mGradientColors = new int[3];
@@ -1943,6 +1960,10 @@
}
}
+ public boolean hasCenterColor() {
+ return mGradientColors != null && mGradientColors.length == 3;
+ }
+
private void applyDensityScaling(int sourceDensity, int targetDensity) {
if (mInnerRadius > 0) {
mInnerRadius = Drawable.scaleFromDensity(
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index 82fc7ac..af517d6 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.util.Preconditions;
@@ -232,6 +233,16 @@
return mSlant;
}
+ /**
+ * Compute the matching score for another style.
+ *
+ * The smaller is better.
+ * @hide
+ */
+ public int getMatchScore(@NonNull FontStyle o) {
+ return Math.abs((getWeight() - o.getWeight())) / 100 + (getSlant() == o.getSlant() ? 0 : 2);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1cb0d25..365be10 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -349,7 +349,7 @@
goto exit;
}
}
- ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+ ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc,
targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
}
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/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 76db18d..63b2527 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -26,7 +26,9 @@
#include <algorithm>
#include <limits>
+#include <map>
#include <memory>
+#include <set>
#include <type_traits>
#include <android-base/macros.h>
@@ -7033,178 +7035,206 @@
return NO_ERROR;
}
-struct IdmapTypeMap {
- ssize_t overlayTypeId;
- size_t entryOffset;
- Vector<uint32_t> entryMap;
+class IdmapMatchingResources;
+
+class IdmapTypeMapping {
+public:
+ void add(uint32_t targetResId, uint32_t overlayResId) {
+ uint8_t targetTypeId = Res_GETTYPE(targetResId);
+ if (mData.find(targetTypeId) == mData.end()) {
+ mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
+ }
+ auto& entries = mData[targetTypeId];
+ entries.insert(std::make_pair(targetResId, overlayResId));
+ }
+
+ bool empty() const {
+ return mData.empty();
+ }
+
+private:
+ // resource type ID in context of target -> set of resource entries mapping target -> overlay
+ std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> mData;
+
+ friend IdmapMatchingResources;
};
-status_t ResTable::createIdmap(const ResTable& overlay,
+class IdmapMatchingResources {
+public:
+ IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm) : mTypeMapping(std::move(tm)) {
+ assert(mTypeMapping);
+ for (auto ti = mTypeMapping->mData.cbegin(); ti != mTypeMapping->mData.cend(); ++ti) {
+ uint32_t lastSeen = 0xffffffff;
+ size_t totalEntries = 0;
+ for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
+ assert(lastSeen == 0xffffffff || lastSeen < ei->first);
+ mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
+ lastSeen = ei->first;
+ totalEntries += 1 + mEntryPadding[ei->first];
+ }
+ mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
+ }
+ }
+
+ const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
+ return mTypeMapping->mData;
+ }
+
+ size_t getNumberOfEntriesIncludingPadding(uint8_t type) const {
+ return mNumberOfEntriesIncludingPadding.at(type);
+ }
+
+ size_t getPadding(uint32_t resid) const {
+ return mEntryPadding.at(resid);
+ }
+
+private:
+ // resource type ID in context of target -> set of resource entries mapping target -> overlay
+ const std::unique_ptr<IdmapTypeMapping> mTypeMapping;
+
+ // resource ID in context of target -> trailing padding for that resource (call FixPadding
+ // before use)
+ std::map<uint32_t, size_t> mEntryPadding;
+
+ // resource type ID in context of target -> total number of entries, including padding entries,
+ // for that type (call FixPadding before use)
+ std::map<uint8_t, size_t> mNumberOfEntriesIncludingPadding;
+};
+
+status_t ResTable::createIdmap(const ResTable& targetResTable,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
void** outData, size_t* outSize) const
{
- // see README for details on the format of map
- if (mPackageGroups.size() == 0) {
- ALOGW("idmap: target package has no package groups, cannot create idmap\n");
+ if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
+ ALOGE("idmap: unexpected NULL parameter");
+ return UNKNOWN_ERROR;
+ }
+ if (strlen(targetPath) > 255) {
+ ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
+ return UNKNOWN_ERROR;
+ }
+ if (strlen(overlayPath) > 255) {
+ ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
+ return UNKNOWN_ERROR;
+ }
+ if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
+ ALOGE("idmap: invalid overlay package");
+ return UNKNOWN_ERROR;
+ }
+ if (targetResTable.mPackageGroups.size() == 0 ||
+ targetResTable.mPackageGroups[0]->packages.size() == 0) {
+ ALOGE("idmap: invalid target package");
return UNKNOWN_ERROR;
}
- if (mPackageGroups[0]->packages.size() == 0) {
- ALOGW("idmap: target package has no packages in its first package group, "
- "cannot create idmap\n");
- return UNKNOWN_ERROR;
- }
+ const ResTable_package* targetPackageStruct =
+ targetResTable.mPackageGroups[0]->packages[0]->package;
+ const size_t tmpNameSize = arraysize(targetPackageStruct->name);
+ char16_t tmpName[tmpNameSize];
+ strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
+ const String16 targetPackageName(tmpName);
- // The number of resources overlaid that were not explicitly marked overlayable.
- size_t forcedOverlayCount = 0u;
+ const PackageGroup* packageGroup = mPackageGroups[0];
- KeyedVector<uint8_t, IdmapTypeMap> map;
-
- // overlaid packages are assumed to contain only one package group
- const PackageGroup* pg = mPackageGroups[0];
-
- // starting size is header
- *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
-
- // target package id and number of types in map
- *outSize += 2 * sizeof(uint16_t);
-
- // overlay packages are assumed to contain only one package group
- const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
- char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
- strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
- const String16 overlayPackage(tmpName);
-
- for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
- const TypeList& typeList = pg->types[typeIndex];
+ // find the resources that exist in both packages
+ auto typeMapping = std::make_unique<IdmapTypeMapping>();
+ for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
+ const TypeList& typeList = packageGroup->types[typeIndex];
if (typeList.isEmpty()) {
continue;
}
-
const Type* typeConfigs = typeList[0];
- IdmapTypeMap typeMap;
- typeMap.overlayTypeId = -1;
- typeMap.entryOffset = 0;
-
for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
- uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
- resource_name resName;
- if (!this->getResourceName(resID, false, &resName)) {
- if (typeMap.entryMap.isEmpty()) {
- typeMap.entryOffset++;
- }
+ uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
+ resource_name current_res;
+ if (!getResourceName(overlay_resid, false, ¤t_res)) {
continue;
}
uint32_t typeSpecFlags = 0u;
- const String16 overlayType(resName.type, resName.typeLen);
- const String16 overlayName(resName.name, resName.nameLen);
- uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
- overlayName.size(),
- overlayType.string(),
- overlayType.size(),
- overlayPackage.string(),
- overlayPackage.size(),
- &typeSpecFlags);
- if (overlayResID == 0) {
- // No such target resource was found.
- if (typeMap.entryMap.isEmpty()) {
- typeMap.entryOffset++;
- }
+ const uint32_t target_resid = targetResTable.identifierForName(
+ current_res.name,
+ current_res.nameLen,
+ current_res.type,
+ current_res.typeLen,
+ targetPackageName.string(),
+ targetPackageName.size(),
+ &typeSpecFlags);
+
+ if (target_resid == 0) {
continue;
}
- // Now that we know this is being overlaid, check if it can be, and emit a warning if
- // it can't.
- if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
- ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
- forcedOverlayCount++;
- }
-
- if (typeMap.overlayTypeId == -1) {
- typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
- }
-
- if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
- ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
- " but entries should map to resources of type %02zx",
- resID, overlayResID, typeMap.overlayTypeId);
- return BAD_TYPE;
- }
-
- if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
- // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
- size_t index = typeMap.entryMap.size();
- size_t numItems = entryIndex - (typeMap.entryOffset + index);
- if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
- return NO_MEMORY;
- }
- }
- typeMap.entryMap.add(Res_GETENTRY(overlayResID));
- }
-
- if (!typeMap.entryMap.isEmpty()) {
- if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
- return NO_MEMORY;
- }
- *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
+ typeMapping->add(target_resid, overlay_resid);
}
}
- if (map.isEmpty()) {
- ALOGW("idmap: no resources in overlay package present in base package");
+ if (typeMapping->empty()) {
+ ALOGE("idmap: no matching resources");
return UNKNOWN_ERROR;
}
+ const IdmapMatchingResources matchingResources(std::move(typeMapping));
+
+ // write idmap
+ *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
+ *outSize += 2 * sizeof(uint16_t); // target package id, type count
+ auto fixedTypeMapping = matchingResources.getTypeMapping();
+ const auto typesEnd = fixedTypeMapping.cend();
+ for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
+ *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
+ *outSize += matchingResources.getNumberOfEntriesIncludingPadding(ti->first) *
+ sizeof(uint32_t); // entries
+ }
if ((*outData = malloc(*outSize)) == NULL) {
return NO_MEMORY;
}
- uint32_t* data = (uint32_t*)*outData;
- *data++ = htodl(IDMAP_MAGIC);
- *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
- *data++ = htodl(targetCrc);
- *data++ = htodl(overlayCrc);
- const char* paths[] = { targetPath, overlayPath };
- for (int j = 0; j < 2; ++j) {
- char* p = (char*)data;
- const char* path = paths[j];
- const size_t I = strlen(path);
- if (I > 255) {
- ALOGV("path exceeds expected 255 characters: %s\n", path);
- return UNKNOWN_ERROR;
- }
- for (size_t i = 0; i < 256; ++i) {
- *p++ = i < I ? path[i] : '\0';
- }
- data += 256 / sizeof(uint32_t);
- }
- const size_t mapSize = map.size();
- uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
- *typeData++ = htods(pg->id);
- *typeData++ = htods(mapSize);
- for (size_t i = 0; i < mapSize; ++i) {
- uint8_t targetTypeId = map.keyAt(i);
- const IdmapTypeMap& typeMap = map[i];
- *typeData++ = htods(targetTypeId + 1);
- *typeData++ = htods(typeMap.overlayTypeId);
- *typeData++ = htods(typeMap.entryMap.size());
- *typeData++ = htods(typeMap.entryOffset);
+ // write idmap header
+ uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
+ *data++ = htodl(IDMAP_MAGIC); // write: magic
+ *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
+ *data++ = htodl(targetCrc); // write: target crc
+ *data++ = htodl(overlayCrc); // write: overlay crc
- const size_t entryCount = typeMap.entryMap.size();
- uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
- for (size_t j = 0; j < entryCount; j++) {
- entries[j] = htodl(typeMap.entryMap[j]);
+ char* charData = reinterpret_cast<char*>(data);
+ size_t pathLen = strlen(targetPath);
+ for (size_t i = 0; i < 256; ++i) {
+ *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
+ }
+ pathLen = strlen(overlayPath);
+ for (size_t i = 0; i < 256; ++i) {
+ *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
+ }
+ data += (2 * 256) / sizeof(uint32_t);
+
+ // write idmap data header
+ uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+ *typeData++ = htods(targetPackageStruct->id); // write: target package id
+ *typeData++ =
+ htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
+
+ // write idmap data
+ for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
+ const size_t entryCount = matchingResources.getNumberOfEntriesIncludingPadding(ti->first);
+ auto ei = ti->second.cbegin();
+ *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
+ *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
+ *typeData++ = htods(entryCount); // write: entry count
+ *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
+ uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
+ for (; ei != ti->second.cend(); ++ei) {
+ const size_t padding = matchingResources.getPadding(ei->first);
+ for (size_t i = 0; i < padding; ++i) {
+ *entryData++ = htodl(0xffffffff); // write: padding
+ }
+ *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
}
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 59abad4..91261aa 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 {
+ // 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;
@@ -1990,7 +2031,7 @@
// Return value: on success: NO_ERROR; caller is responsible for free-ing
// outData (using free(3)). On failure, any status_t value other than
// NO_ERROR; the caller should not free outData.
- status_t createIdmap(const ResTable& overlay,
+ status_t createIdmap(const ResTable& targetResTable,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
void** outData, size_t* outSize) const;
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 26d2896..10b83a7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -40,7 +40,7 @@
ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
char target_name[256] = "com.android.basic";
- ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name,
+ ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
&data_, &data_size_));
}
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 17d2db71..ed7d5e5 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,8 +9,6 @@
"hwui_lto",
],
- cpp_std: "c++17",
-
cflags: [
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
@@ -45,9 +43,6 @@
],
product_variables: {
- device_uses_hwc2: {
- cflags: ["-DUSE_HWC2"],
- },
eng: {
lto: {
never: true,
@@ -176,6 +171,7 @@
"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",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 06e937a..0cfaa8c 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -146,4 +146,4 @@
return shouldInvert;
}
-}; // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index b772e5b..3bee301 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -85,19 +85,18 @@
mUpdateTexImage = false;
sk_sp<SkImage> layerImage;
SkMatrix textureTransform;
- android_dataspace dataSpace;
bool queueEmpty = true;
// If the SurfaceTexture queue is in synchronous mode, need to discard all
// but latest frame. Since we can't tell which mode it is in,
// do this unconditionally.
do {
- layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty,
+ layerImage = mSurfaceTexture->dequeueImage(textureTransform, &queueEmpty,
mRenderState);
} while (layerImage.get() && (!queueEmpty));
if (layerImage.get()) {
// force filtration if buffer size != layer size
bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height();
- updateLayer(forceFilter, textureTransform, dataSpace, layerImage);
+ updateLayer(forceFilter, textureTransform, layerImage);
}
}
@@ -109,12 +108,11 @@
}
void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- android_dataspace dataspace, const sk_sp<SkImage>& layerImage) {
+ const sk_sp<SkImage>& layerImage) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
mLayer->getTexTransform() = textureTransform;
- mLayer->setDataSpace(dataspace);
mLayer->setImage(layerImage);
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b2c5131..a91c111 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -95,7 +95,7 @@
void detachSurfaceTexture();
void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- android_dataspace dataspace, const sk_sp<SkImage>& layerImage);
+ const sk_sp<SkImage>& layerImage);
void destroyLayer();
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a952cc2..dc63e5d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -31,5 +31,5 @@
*/
using DisplayList = skiapipeline::SkiaDisplayList;
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ba72e93..237fc62 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -26,5 +26,5 @@
virtual void notify(const int64_t* buffer);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index d920a99..75b8038 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -56,5 +56,5 @@
std::vector<sp<FrameMetricsObserver> > mObservers;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/GlFunctorLifecycleListener.h
index 5d07b46..5adc469 100644
--- a/libs/hwui/GlFunctorLifecycleListener.h
+++ b/libs/hwui/GlFunctorLifecycleListener.h
@@ -28,5 +28,5 @@
virtual void onGlFunctorReleased(Functor* functor) = 0;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 165fc48..a97c12c 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -256,4 +256,4 @@
return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
}
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index c0113d8..6298013 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -25,4 +25,4 @@
static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
};
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e7ae767..ccbb6c1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -82,7 +82,6 @@
JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) {
mGlobalData = globalData;
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
-#if USE_HWC2
nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset;
// There are two different offset cases. If the offsetDelta is positive
@@ -96,7 +95,6 @@
// return due to the staggering of VSYNC-app & VSYNC-sf.
mDequeueTimeForgiveness = offsetDelta + 4_ms;
}
-#endif
setFrameInterval(frameIntervalNanos);
}
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 32aaa54..a15ff22 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -33,7 +33,6 @@
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
- buildColorSpaceWithFilter();
renderState.registerLayer(this);
texTransform.setIdentity();
transform.setIdentity();
@@ -43,36 +42,6 @@
mRenderState.unregisterLayer(this);
}
-void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
- if (filter != mColorFilter) {
- mColorFilter = filter;
- buildColorSpaceWithFilter();
- }
-}
-
-void Layer::setDataSpace(android_dataspace dataspace) {
- if (dataspace != mCurrentDataspace) {
- mCurrentDataspace = dataspace;
- buildColorSpaceWithFilter();
- }
-}
-
-void Layer::buildColorSpaceWithFilter() {
- sk_sp<SkColorFilter> colorSpaceFilter;
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
- if (colorSpace && !colorSpace->isSRGB()) {
- colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
- }
-
- if (mColorFilter && colorSpaceFilter) {
- mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
- } else if (colorSpaceFilter) {
- mColorSpaceWithFilter = colorSpaceFilter;
- } else {
- mColorSpaceWithFilter = mColorFilter;
- }
-}
-
void Layer::postDecStrong() {
mRenderState.postDecStrong(this);
}
@@ -85,5 +54,5 @@
}
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e4f96e9..ea3bfc9 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -69,15 +69,9 @@
SkBlendMode getMode() const;
- inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+ inline sk_sp<SkColorFilter> getColorFilter() const { return mColorFilter; }
- void setColorFilter(sk_sp<SkColorFilter> filter);
-
- void setDataSpace(android_dataspace dataspace);
-
- void setColorSpace(sk_sp<SkColorSpace> colorSpace);
-
- inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
+ void setColorFilter(sk_sp<SkColorFilter> filter) { mColorFilter = filter; };
inline SkMatrix& getTexTransform() { return texTransform; }
@@ -98,24 +92,12 @@
RenderState& mRenderState;
private:
- void buildColorSpaceWithFilter();
-
/**
* Color filter used to draw this layer. Optional.
*/
sk_sp<SkColorFilter> mColorFilter;
/**
- * Colorspace of the contents of the layer. Optional.
- */
- android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
-
- /**
- * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
- */
- sk_sp<SkColorFilter> mColorSpaceWithFilter;
-
- /**
* Indicates raster data backing the layer is scaled, requiring filtration.
*/
bool forceFilter = false;
@@ -162,5 +144,5 @@
}; // struct Layer
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index 6857999..2c63af6 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -50,7 +50,7 @@
std::vector<Entry> mEntries;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
diff --git a/libs/hwui/Lighting.h b/libs/hwui/Lighting.h
index d972c21..ccfbb93 100644
--- a/libs/hwui/Lighting.h
+++ b/libs/hwui/Lighting.h
@@ -34,5 +34,5 @@
uint8_t spotShadowAlpha;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index d84ed32..d0dbff03 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -526,5 +526,5 @@
ALOGD("]");
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index f0a3a95..b33cfe2 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -27,6 +27,7 @@
namespace uirenderer {
#define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define SK_MATRIX_STRING_V "[%.9f %.9f %.9f] [%.9f %.9f %.9f] [%.9f %.9f %.9f]"
#define SK_MATRIX_ARGS(m) \
(m)->get(0), (m)->get(1), (m)->get(2), (m)->get(3), (m)->get(4), (m)->get(5), (m)->get(6), \
(m)->get(7), (m)->get(8)
@@ -244,5 +245,5 @@
typedef Matrix4 mat4;
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index 082e95f..86d3cb9 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -103,5 +103,5 @@
}
}
-}; // namespace NinePatchUtils
-}; // namespace android
+} // namespace NinePatchUtils
+} // namespace android
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index ad599e9..808921d 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -304,5 +304,5 @@
return;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 474eb97..f5bebce 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -46,6 +46,6 @@
static void validateVerbAndPoints(char verb, size_t points, ParseResult* result);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_PATHPARSER_H
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 3f2c616..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) {
@@ -223,5 +198,5 @@
sRenderPipelineType = type;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 542bc71..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);
@@ -289,7 +257,7 @@
static RenderPipelineType sRenderPipelineType;
}; // class Caches
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_PROPERTIES_H
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 80f2b57..2a48837 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -66,13 +66,10 @@
sk_sp<SkColorSpace> colorSpace =
DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
- sk_sp<SkColorFilter> colorSpaceFilter;
- if (colorSpace && !colorSpace->isSRGB()) {
- colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
- }
sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
- return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+ reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()),
+ kPremul_SkAlphaType, colorSpace);
+ return copyImageInto(image, texTransform, srcRect, bitmap);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -83,20 +80,7 @@
transform.loadScale(1, -1, 1);
transform.translate(0, -1);
- // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
- // TODO: When this was attempted, it resulted in instability.
- sk_sp<SkColorFilter> colorSpaceFilter;
- sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
- if (colorSpace && !colorSpace->isSRGB()) {
- colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
- }
- sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
-
- // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
- // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
- // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
- return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+ return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap);
}
CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
@@ -118,8 +102,7 @@
return copyResult;
}
-CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
- sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
const Rect& srcRect, SkBitmap* bitmap) {
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
@@ -157,11 +140,7 @@
return copyResult;
}
- // See Readback::copyLayerInto for an overview of color space conversion.
- // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
- // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
- // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
- Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+ Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
layer.setForceFilter(!disableFilter);
@@ -177,38 +156,6 @@
bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
SkBitmap* bitmap) {
- /*
- * In the past only TextureView readback was setting the temporary surface color space to null.
- * Now all 3 readback flows are drawing into a SkSurface with null color space.
- * At readback there are 3 options to convert the source image color space to the destination
- * color space requested in "bitmap->info().colorSpace()":
- * 1. Set color space for temporary surface render target to null (disables color management),
- * colorspace tag from source SkImage is ignored by Skia,
- * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
- * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
- * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
- *
- * 2. Set color space for temporary surface render target to SRGB (not nullptr),
- * colorspace tag on the source SkImage is used by Skia to enable conversion,
- * convert SkImage to SRGB at draw time with drawImage (no filters),
- * do a readback from temporary SkSurface, which will do a color conversion from SRGB to
- * bitmap->info().colorSpace() on the CPU.
- *
- * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
- * colorspace tag on the source SkImage is used by Skia to enable conversion,
- * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
- * do a readback from SkSurface, which will not do any color conversion, because
- * surface was created with the same color space as the "bitmap".
- *
- * Option 1 is used for all readback flows.
- * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
- * linear blending.
- * TODO: evaluate if options 2 or 3 for color space conversion are better.
- */
-
- // drop the colorSpace from the temporary surface.
- SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
/* This intermediate surface is present to work around a bug in SwiftShader that
* prevents us from reading the contents of the layer's texture directly. The
* workaround involves first rendering that texture into an intermediate buffer and
@@ -217,70 +164,44 @@
* with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
*/
sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- SkBudgeted::kYes, surfaceInfo);
+ SkBudgeted::kYes, bitmap->info());
+ // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
+ // attempt to do the intermediate rendering step in 8888
if (!tmpSurface.get()) {
- surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+ SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
- surfaceInfo);
+ tmpInfo);
if (!tmpSurface.get()) {
- ALOGW("Unable to readback GPU contents into the provided bitmap");
+ ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
return false;
}
}
- if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
- tmpSurface->getCanvas(), layer, srcRect, dstRect,
- false)) {
- // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
- // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
- // disabled color conversion.
- SkColorSpace* destColorSpace = bitmap->info().colorSpace();
- SkBitmap tempSRGBBitmap;
- SkBitmap tmpN32Bitmap;
- SkBitmap* bitmapInSRGB;
- if (destColorSpace && !destColorSpace->isSRGB()) {
- tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
- bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB.
- } else {
- bitmapInSRGB = bitmap; // No need for color conversion - write directly into output.
- }
- bool success = false;
+ if (!skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+ tmpSurface->getCanvas(), layer, srcRect, dstRect,
+ false)) {
+ ALOGW("Unable to draw content from GPU into the provided bitmap");
+ return false;
+ }
- // TODO: does any of the readbacks below clamp F16 exSRGB?
- // Readback into a SRGB SkBitmap.
- if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
- bitmapInSRGB->rowBytes(), 0, 0)) {
- success = true;
- } else {
- // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
- // 8888 and then convert that into the destination format before giving up.
- SkImageInfo bitmapInfo =
- SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
- SkColorSpace::MakeSRGB());
- if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
- tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
- tmpN32Bitmap.rowBytes(), 0, 0)) {
- success = true;
- bitmapInSRGB = &tmpN32Bitmap;
- }
- }
-
- if (success) {
- if (bitmapInSRGB != bitmap) {
- // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
- // destination bitmap color format if needed.
- if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
- bitmap->rowBytes(), 0, 0)) {
- return false;
- }
- }
- bitmap->notifyPixelsChanged();
- return true;
+ if (!tmpSurface->readPixels(*bitmap, 0, 0)) {
+ // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+ // 8888 and then convert that into the destination format before giving up.
+ SkBitmap tmpBitmap;
+ SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
+ if (bitmap->info().colorType() == SkColorType::kN32_SkColorType ||
+ !tmpBitmap.tryAllocPixels(tmpInfo) ||
+ !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
+ !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(),
+ bitmap->rowBytes(), 0, 0)) {
+ ALOGW("Unable to convert content into the provided bitmap");
+ return false;
}
}
- return false;
+ bitmap->notifyPixelsChanged();
+ return true;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index d9e10ce..e86a813 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -54,8 +54,8 @@
CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
private:
- CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
- Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+ CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
+ const Rect& srcRect, SkBitmap* bitmap);
bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
SkBitmap* bitmap);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f928de9..c63e449 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -1028,5 +1028,5 @@
fDL->drawVectorDrawable(tree);
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 099e0be..08cfc62 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -216,5 +216,5 @@
DisplayListData* fDL;
};
-}; // namespace uirenderer
-}; // namespace android
\ No newline at end of file
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 0715187..d6362ef 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -262,5 +262,5 @@
}
}; // class Rect
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d2a8f02..00ce28a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -29,6 +29,7 @@
#include <SkPathOps.h>
#include <algorithm>
+#include <atomic>
#include <sstream>
#include <string>
@@ -47,8 +48,14 @@
TreeInfo* mTreeInfo;
};
+static int64_t generateId() {
+ static std::atomic<int64_t> sNextId{1};
+ return sNextId++;
+}
+
RenderNode::RenderNode()
- : mDirtyPropertyFields(0)
+ : mUniqueId(generateId())
+ , mDirtyPropertyFields(0)
, mNeedsDisplayListSync(false)
, mDisplayList(nullptr)
, mStagingDisplayList(nullptr)
@@ -444,5 +451,42 @@
return &mClippedOutlineCache.clippedOutline;
}
+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) {
+ int needed = snprintf(buffer.data(), buffer.size(),
+ format.data(), std::forward<T>(args)...);
+ if (needed < 0) {
+ buffer[0] = '\0';
+ buffer.resize(1);
+ return;
+ }
+ if (needed < buffer.size()) {
+ buffer.resize(needed + 1);
+ return;
+ }
+ // 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=%" 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=%" PRId64 ", name='%s')", uniqueId(), getName());
+ canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index be0b46b..6060123 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -213,6 +213,11 @@
UsageHint usageHint() const { return mUsageHint; }
+ int64_t uniqueId() const { return mUniqueId; }
+
+ void markDrawStart(SkCanvas& canvas);
+ void markDrawEnd(SkCanvas& canvas);
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -233,6 +238,7 @@
void incParentRefCount() { mParentCount++; }
void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr);
+ const int64_t mUniqueId;
String8 mName;
sp<VirtualLightRefBase> mUserContext;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9a15ff2..6be7ef7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -27,7 +27,6 @@
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
-#include <SkColorSpaceXformCanvas.h>
#include <SkDeque.h>
#include <SkDrawable.h>
#include <SkFont.h>
@@ -61,19 +60,8 @@
SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {}
SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
- sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
- mCanvasOwned =
- std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
- if (cs.get() == nullptr || cs->isSRGB()) {
- mCanvas = mCanvasOwned.get();
- } else {
- /** The wrapper is needed if we are drawing into a non-sRGB destination, since
- * we need to transform all colors (not just bitmaps via filters) into the
- * destination's colorspace.
- */
- mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs));
- mCanvas = mCanvasWrapper.get();
- }
+ mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+ mCanvas = mCanvasOwned.get();
}
SkiaCanvas::~SkiaCanvas() {}
@@ -82,7 +70,6 @@
if (mCanvas != skiaCanvas) {
mCanvas = skiaCanvas;
mCanvasOwned.reset();
- mCanvasWrapper.reset();
}
mSaveStack.reset(nullptr);
}
@@ -92,18 +79,9 @@
// ----------------------------------------------------------------------------
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
- sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
- std::unique_ptr<SkCanvas> newCanvas =
- std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
- std::unique_ptr<SkCanvas> newCanvasWrapper;
- if (cs.get() != nullptr && !cs->isSRGB()) {
- newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
- }
-
// deletes the previously owned canvas (if any)
- mCanvasOwned = std::move(newCanvas);
- mCanvasWrapper = std::move(newCanvasWrapper);
- mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get();
+ mCanvasOwned.reset(new SkCanvas(bitmap));
+ mCanvas = mCanvasOwned.get();
// clean up the old save stack
mSaveStack.reset(nullptr);
@@ -548,40 +526,14 @@
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
- sk_sp<SkColorFilter> colorSpaceFilter) const {
- /* We don't apply the colorSpace filter if this canvas is already wrapped with
- * a SkColorSpaceXformCanvas since it already takes care of converting the
- * contents of the bitmap into the appropriate colorspace. The mCanvasWrapper
- * should only be used if this canvas is backed by a surface/bitmap that is known
- * to have a non-sRGB colorspace.
- */
- if (!mCanvasWrapper && colorSpaceFilter) {
- SkPaint& tmpPaint = paint.writeable();
- if (tmpPaint.getColorFilter()) {
- tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
- std::move(colorSpaceFilter)));
- LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
- } else {
- tmpPaint.setColorFilter(std::move(colorSpaceFilter));
- }
- }
- return filterPaint(std::move(paint));
-}
-
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
+ mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
-
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
+ mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -590,9 +542,7 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+ mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint),
SkCanvas::kFast_SrcRectConstraint);
}
@@ -674,13 +624,9 @@
PaintCoW paintCoW(paint);
SkPaint& tmpPaint = paintCoW.writeable();
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ sk_sp<SkImage> image = bitmap.makeImage();
sk_sp<SkShader> shader =
image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
- if (colorFilter) {
- shader = shader->makeWithColorFilter(std::move(colorFilter));
- }
tmpPaint.setShader(std::move(shader));
mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
@@ -711,10 +657,7 @@
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageLattice(image.get(), lattice, dst,
- filterBitmap(paint, std::move(colorFilter)));
+ mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint));
}
double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -738,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 &&
@@ -765,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/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3a877cf..24d9c08 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -232,7 +232,6 @@
class Clip;
- std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas
std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated
SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us
// unless it is the same as mCanvasOwned.get()
diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h
index b495e33..833ca4a 100644
--- a/libs/hwui/UvMapper.h
+++ b/libs/hwui/UvMapper.h
@@ -124,7 +124,7 @@
float mMaxV;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_UV_MAPPER_H
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index d2c15ad..e6eea1c 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -113,7 +113,7 @@
}
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_VECTOR_H
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 6cf04bf..dd62bbb 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -691,7 +691,7 @@
return BitmapPalette::Unknown;
}
-}; // namespace VectorDrawable
+} // namespace VectorDrawable
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index f091277..28cabb9 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -73,7 +73,7 @@
REQUIRE_COMPATIBLE_LAYOUT(TextureVertex);
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_VERTEX_H
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 613cf4a..6543a22 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -174,7 +174,7 @@
void (*mCleanupIndexMethod)(void*);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_VERTEX_BUFFER_H
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 75a6e72..6c77f9e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -32,7 +32,6 @@
#include <SkCanvas.h>
#include <SkImagePriv.h>
-#include <SkToSRGBColorFilter.h>
#include <SkHighContrastFilter.h>
#include <limits>
@@ -287,14 +286,8 @@
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
if (isHardware()) {
- outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
- info().colorType(), info().alphaType(), nullptr));
+ outBitmap->allocPixels(mInfo);
uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
- if (mInfo.colorSpace()) {
- sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
- outBitmap->setInfo(mInfo);
- outBitmap->setPixelRef(std::move(pixelRef), 0, 0);
- }
return;
}
outBitmap->setInfo(mInfo, rowBytes());
@@ -313,7 +306,7 @@
return nullptr;
}
-sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) {
+sk_sp<SkImage> Bitmap::makeImage() {
sk_sp<SkImage> image = mImage;
if (!image) {
SkASSERT(!isHardware());
@@ -325,9 +318,6 @@
// TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
}
- if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
- *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
- }
return image;
}
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 238c764..d446377 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -105,14 +105,8 @@
* Creates or returns a cached SkImage and is safe to be invoked from either
* the UI or RenderThread.
*
- * @param outputColorFilter is a required param that will be populated by
- * this function if the bitmap's colorspace is not sRGB. If populated the
- * filter will convert colors from the bitmaps colorspace into sRGB. It
- * is the callers responsibility to use this colorFilter when drawing
- * this image into any destination that is presumed to be sRGB (i.e. a
- * buffer that has no colorspace defined).
*/
- sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+ sk_sp<SkImage> makeImage();
static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index e99742b..a5f21d8 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -76,8 +76,8 @@
namespace uirenderer {
namespace VectorDrawable {
class Tree;
-};
-};
+}
+}
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
@@ -318,4 +318,4 @@
friend class DrawTextOnPathFunctor;
};
-}; // namespace android
+} // namespace android
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/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index efef6de..bf19655 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -79,6 +79,6 @@
sp<uirenderer::CanvasPropertyPaint> mPaint;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index e4ba13d..2062194 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -172,6 +172,6 @@
std::string mIdent;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 162d137..af3a056 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -48,6 +48,6 @@
const SkRect mBounds;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 90d5e71..4a87e75 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -216,6 +216,6 @@
}
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index b06f7f0..215979c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -41,6 +41,6 @@
void onDraw(SkCanvas* canvas) override;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 13d2dae..f08ac17 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <utils/MathUtils.h>
#include "LayerDrawable.h"
#include "GrBackendSurface.h"
@@ -32,6 +33,24 @@
}
}
+// This is a less-strict matrix.isTranslate() that will still report being translate-only
+// on imperceptibly small scaleX & scaleY values.
+static bool isBasicallyTranslate(const SkMatrix& matrix) {
+ if (!matrix.isScaleTranslate()) return false;
+ return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+}
+
+static bool shouldFilter(const SkMatrix& matrix) {
+ if (!matrix.isScaleTranslate()) return true;
+
+ // We only care about meaningful scale here
+ bool noScale = MathUtils::isOne(matrix.getScaleX())
+ && MathUtils::isOne(matrix.getScaleY());
+ bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
+ && SkScalarIsInt(matrix.getTranslateY());
+ return !(noScale && pixelAligned);
+}
+
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
const SkRect* srcRect, const SkRect* dstRect,
bool useLayerTransform) {
@@ -70,7 +89,7 @@
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
- paint.setColorFilter(layer->getColorSpaceWithFilter());
+ paint.setColorFilter(layer->getColorFilter());
const bool nonIdentityMatrix = !matrix.isIdentity();
if (nonIdentityMatrix) {
canvas->save();
@@ -101,7 +120,7 @@
// Integer translation is defined as when src rect and dst rect align fractionally.
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
// only for SrcOver blending and without color filter (readback uses Src blending).
- bool isIntegerTranslate = totalMatrix.isTranslate()
+ bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
&& SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
== SkScalarFraction(skiaSrcRect.fLeft)
&& SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
@@ -112,10 +131,7 @@
canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
- bool isIntegerTranslate = totalMatrix.isTranslate()
- && SkScalarIsInt(totalMatrix[SkMatrix::kMTransX])
- && SkScalarIsInt(totalMatrix[SkMatrix::kMTransY]);
- if (layer->getForceFilter() || !isIntegerTranslate) {
+ if (layer->getForceFilter() || shouldFilter(totalMatrix)) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
canvas->drawImage(layerImage.get(), 0, 0, &paint);
@@ -129,6 +145,6 @@
return layerImage != nullptr;
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 5c12590..95dc6d0 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -45,6 +45,6 @@
sp<DeferredLayerUpdater> mLayerUpdater;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index ea14d11..4494cb0 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -115,12 +115,26 @@
}
}
+class MarkDraw {
+public:
+ explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ mNode.markDrawStart(mCanvas);
+ }
+ }
+ ~MarkDraw() {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ mNode.markDrawEnd(mCanvas);
+ }
+ }
+private:
+ SkCanvas& mCanvas;
+ RenderNode& mNode;
+};
+
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
RenderNode* renderNode = mRenderNode.get();
- if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
- SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
- canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
- }
+ MarkDraw _marker{*canvas, *renderNode};
// We only respect the nothingToDraw check when we are composing a layer. This
// ensures that we paint the layer even if it is not currently visible in the
@@ -318,6 +332,6 @@
}
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index d746978..6ba8e59 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -150,6 +150,6 @@
SkiaDisplayList* mProjectedDisplayList = nullptr;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index dba97fe..0a3c8f4 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -211,6 +211,6 @@
casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index 26cfa90..cfc0f9b 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -74,6 +74,6 @@
StartReorderBarrierDrawable* mStartBarrier;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 3890513..ac6f6a3 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -141,6 +141,6 @@
mDisplayList.draw(&canvas);
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index ac7bb7b..d7879e7 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -36,7 +36,7 @@
namespace VectorDrawable {
class Tree;
-};
+}
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
namespace skiapipeline {
@@ -179,6 +179,6 @@
SkMatrix mParentMatrix;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 6ae5999..142bca9 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -97,7 +97,7 @@
SkASSERT(mRenderThread.getGrContext() != nullptr);
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
- nullptr, &props));
+ mSurfaceColorSpace, &props));
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
@@ -176,6 +176,7 @@
} else if (colorMode == ColorMode::WideColorGamut) {
mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
}
+ mSurfaceColorSpace = SkColorSpace::MakeSRGB();
if (mEglSurface != EGL_NO_SURFACE) {
const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 2dfe7c7..7a255c1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -169,7 +169,7 @@
if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
SkImageInfo info;
info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
- kPremul_SkAlphaType);
+ kPremul_SkAlphaType, getSurfaceColorSpace());
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
@@ -204,8 +204,7 @@
GrContext* context = thread.getGrContext();
if (context) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
- sk_sp<SkColorFilter> colorFilter;
- auto image = bitmap->makeImage(&colorFilter);
+ auto image = bitmap->makeImage();
if (image.get() && !bitmap->isHardware()) {
SkImage_pinAsTexture(image.get(), context);
SkImage_unpinAsTexture(image.get(), context);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 45022e7..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 {
@@ -179,9 +182,8 @@
}
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+ sk_sp<SkImage> image = bitmap.makeImage();
+ mRecorder.drawImage(image, left, top, filterPaint(paint), bitmap.palette());
// if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
// it is not safe to store a raw SkImage pointer, because the image object will be destroyed
// when this function ends.
@@ -194,9 +196,8 @@
SkAutoCanvasRestore acr(&mRecorder, true);
concat(matrix);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+ sk_sp<SkImage> image = bitmap.makeImage();
+ mRecorder.drawImage(image, 0, 0, filterPaint(paint), bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
@@ -208,9 +209,8 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+ sk_sp<SkImage> image = bitmap.makeImage();
+ mRecorder.drawImageRect(image, srcRect, dstRect, filterPaint(paint),
SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
@@ -247,10 +247,9 @@
if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
}
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ sk_sp<SkImage> image = bitmap.makeImage();
mRecorder.drawImageLattice(image, lattice, dst,
- filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+ filterPaint(std::move(filteredPaint)),
bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
@@ -263,6 +262,6 @@
return 0;
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 988728d..d6107a9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -92,6 +92,6 @@
PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a494e49..3607b23 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -122,8 +122,9 @@
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/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 a594206..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{};
@@ -219,6 +219,6 @@
});
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 3269cfb..8fe52c5 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -51,6 +51,6 @@
SkImageInfo mFBInfo;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h
index efa9da2..9e1bb8e 100644
--- a/libs/hwui/private/hwui/DrawGlInfo.h
+++ b/libs/hwui/private/hwui/DrawGlInfo.h
@@ -83,7 +83,7 @@
};
}; // struct DrawGlInfo
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_DRAW_GL_INFO_H
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..6869972 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -323,25 +323,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;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6668c58..d9b789f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -49,7 +49,7 @@
Reset = 1 << 1,
JankStats = 1 << 2,
};
-};
+}
/*
* RenderProxy is strictly single threaded. All methods must be invoked on the owning
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index d84ec85..2abb3d5 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -263,6 +263,15 @@
tailPNext = &blend->pNext;
}
+ VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeature;
+ ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*) malloc(
+ sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
+ LOG_ALWAYS_FATAL_IF(!ycbcrFeature);
+ ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ ycbcrFeature->pNext = nullptr;
+ *tailPNext = ycbcrFeature;
+ tailPNext = &ycbcrFeature->pNext;
+
// query to get the physical device features
mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
// this looks like it would slow things down,
@@ -463,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;
}
@@ -638,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);
@@ -735,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;
@@ -824,14 +834,15 @@
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/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp
index c8220c6..85b3917 100644
--- a/libs/hwui/surfacetexture/EGLConsumer.cpp
+++ b/libs/hwui/surfacetexture/EGLConsumer.cpp
@@ -672,4 +672,4 @@
return image;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h
index eccb082..7dac3ef 100644
--- a/libs/hwui/surfacetexture/EGLConsumer.h
+++ b/libs/hwui/surfacetexture/EGLConsumer.h
@@ -308,4 +308,4 @@
sp<EglImage> mReleasedTexImage;
};
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 9ffccfb..15aec9f 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -22,6 +22,7 @@
#include "renderthread/EglManager.h"
#include "renderthread/RenderThread.h"
#include "renderthread/VulkanManager.h"
+#include "utils/Color.h"
// Macro for including the SurfaceTexture name in log messages
#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
@@ -44,13 +45,16 @@
mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
}
-void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) {
- if (!mImage.get()) {
+void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
+ android_dataspace dataspace) {
+ if (!mImage.get() || dataspace != mDataspace) {
mImage = graphicBuffer.get()
? SkImage::MakeFromAHardwareBuffer(
reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
- kPremul_SkAlphaType)
+ kPremul_SkAlphaType,
+ uirenderer::DataSpaceToColorSpace(dataspace))
: nullptr;
+ mDataspace = dataspace;
}
}
@@ -66,7 +70,7 @@
int slot = st.mCurrentTexture;
if (slot != BufferItem::INVALID_BUFFER_SLOT) {
*queueEmpty = true;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
return mImageSlots[slot].mImage;
}
}
@@ -145,7 +149,7 @@
st.computeCurrentTransformMatrixLocked();
*queueEmpty = false;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
return mImageSlots[slot].mImage;
}
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
index 31ee8db..f0e55bb 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.h
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -68,18 +68,21 @@
* ImageConsumer maintains about a BufferQueue buffer slot.
*/
struct ImageSlot {
- ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+ ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {}
// mImage is the SkImage created from mGraphicBuffer.
sk_sp<SkImage> mImage;
+ // the dataspace associated with the current image
+ android_dataspace mDataspace;
+
/**
* mEglFence is the EGL sync object that must signal before the buffer
* associated with this buffer slot may be dequeued.
*/
EGLSyncKHR mEglFence;
- void createIfNeeded(sp<GraphicBuffer> graphicBuffer);
+ void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace);
};
/**
@@ -94,4 +97,4 @@
ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
};
-}; /* namespace android */
+} /* namespace android */
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
index 4bff715..da09444 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.cpp
+++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp
@@ -470,8 +470,7 @@
ConsumerBase::dumpLocked(result, prefix);
}
-sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
- bool* queueEmpty,
+sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
uirenderer::RenderState& renderState) {
Mutex::Autolock _l(mMutex);
@@ -488,9 +487,8 @@
auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
if (image.get()) {
uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
- dataSpace = mCurrentDataSpace;
}
return image;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
index db392a9..b5d136f 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.h
+++ b/libs/hwui/surfacetexture/SurfaceTexture.h
@@ -258,8 +258,8 @@
*/
status_t attachToContext(uint32_t tex);
- sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
- bool* queueEmpty, uirenderer::RenderState& renderState);
+ sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
+ uirenderer::RenderState& renderState);
/**
* attachToView attaches a SurfaceTexture that is currently in the
@@ -449,4 +449,4 @@
};
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 66b9b85..f812022 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,9 +72,7 @@
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- SkMatrix identity;
- identity.setIdentity();
- layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr);
+ layerUpdater->updateLayer(true, SkMatrix::I(), nullptr);
return layerUpdater;
}
@@ -83,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
@@ -96,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/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 15039b5..ad11a1d 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -44,8 +44,7 @@
});
SkPaint paint;
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = hwuiBitmap->makeImage(&colorFilter);
+ sk_sp<SkImage> image = hwuiBitmap->makeImage();
sk_sp<SkShader> repeatShader =
image->makeShader(SkShader::TileMode::kRepeat_TileMode,
SkShader::TileMode::kRepeat_TileMode, nullptr);
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index f137562..448408d 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -72,8 +72,7 @@
void doFrame(int frameNr) override {}
sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ sk_sp<SkImage> image = bitmap.makeImage();
return image->makeShader(SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode);
}
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index c235715..210fced 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -54,8 +54,7 @@
// create an image and pin it so that we have something with a unique key in the cache
sk_sp<Bitmap> bitmap =
Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h));
- sk_sp<SkColorFilter> filter;
- sk_sp<SkImage> image = bitmap->makeImage(&filter);
+ sk_sp<SkImage> image = bitmap->makeImage();
ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
// attempt to trim all memory while we still hold strong refs
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 6c8775b..a686979 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -43,7 +43,7 @@
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
- layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage);
+ layerUpdater->updateLayer(true, scaledMatrix, layerImage);
// the backing layer should now have all the properties applied.
EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 217d63f..41714eb 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -81,5 +81,5 @@
EXPECT_TRUE(queue.entries().empty());
}
-};
-};
+}
+}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 634ceff..f3a7648 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -53,12 +53,12 @@
adobeBitmap->getSkBitmap(&adobeSkBitmap);
*adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red
- SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
+ SkImageInfo info = adobeInfo.makeColorSpace(SkColorSpace::MakeSRGB());
sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
- // Create a software canvas.
+ // Create a software sRGB canvas.
SkiaCanvas canvas(skBitmap);
canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
// The result should be fully red, since we convert to sRGB at draw time.
@@ -77,7 +77,7 @@
picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
- // Playback to an software canvas. The result should be fully red.
+ // Playback to a software sRGB canvas. The result should be fully red.
canvas.asSkCanvas()->drawPicture(picture);
ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
}
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 02f740c..ee6beba 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -406,5 +406,5 @@
EXPECT_TRUE(shader->unique());
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
index 8faeee6..bb750ca 100644
--- a/libs/hwui/thread/Barrier.h
+++ b/libs/hwui/thread/Barrier.h
@@ -48,7 +48,7 @@
mutable Condition mCondition;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_BARRIER_H
diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h
index 45f3102..df53348e 100644
--- a/libs/hwui/thread/Future.h
+++ b/libs/hwui/thread/Future.h
@@ -53,7 +53,7 @@
T mResult;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_FUTURE_H
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index ffcd4b6..6d33ac4 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -53,7 +53,7 @@
mutable Condition mCondition;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_SIGNAL_H
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 276a22f..228ce19 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -48,7 +48,7 @@
sp<Future<T> > mFuture;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_TASK_H
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 54b55e4..26ff6eb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -129,5 +129,5 @@
mSignal.signal();
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 29b4fcd..c4c1291 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -101,7 +101,7 @@
std::vector<sp<WorkerThread> > mThreads;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_TASK_MANAGER_H
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index 1bc5646..763d1aa 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -178,5 +178,5 @@
}
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index bec3837..d6b41b8 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -41,7 +41,7 @@
int32_t width, int32_t height);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_BLUR_H
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 3fb6a31..dc347f6 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -221,5 +221,5 @@
static_cast<uint8_t>(rgb.b * 255));
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index eafe2f1..8cc4d10 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -99,7 +99,7 @@
typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_FAT_VECTOR_H
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index fcd036c..c694e93 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -76,5 +76,5 @@
}
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 3e5021c..8baa4b77 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -249,5 +249,5 @@
ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 03f685e..b401fcf 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -201,7 +201,7 @@
: std::vector<T, LinearStdAllocator<T>>(allocator) {}
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 5475898..cc8d83f 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -34,6 +34,10 @@
return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
}
+ inline static bool isOne(float value) {
+ return areEqual(value, 1.0f);
+ }
+
inline static bool isPositive(float value) { return value >= NON_ZERO_EPSILON; }
/**
diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h
index 4bcd576..76f93cb 100644
--- a/libs/hwui/utils/Pair.h
+++ b/libs/hwui/utils/Pair.h
@@ -36,7 +36,7 @@
inline const S& getSecond() const { return second; }
};
-}; // namespace uirenderer
+} // namespace uirenderer
template <typename F, typename S>
struct trait_trivial_ctor<uirenderer::Pair<F, S> > {
@@ -55,6 +55,6 @@
enum { value = aggregate_traits<F, S>::has_trivial_move };
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_HWUI_PAIR_H
diff --git a/libs/hwui/utils/Result.h b/libs/hwui/utils/Result.h
index 7f33f2e..bd20ba6 100644
--- a/libs/hwui/utils/Result.h
+++ b/libs/hwui/utils/Result.h
@@ -51,4 +51,4 @@
std::variant<R, Error<E>> result;
};
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index b3e8931..081386a 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -61,7 +61,7 @@
size_t mCount = 0;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif /* RINGBUFFER_H_ */
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
index 5304b76..304982e 100644
--- a/libs/hwui/utils/StringUtils.cpp
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -34,5 +34,5 @@
return set;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/TypeLogic.h b/libs/hwui/utils/TypeLogic.h
index dbdad33..1689cce 100644
--- a/libs/hwui/utils/TypeLogic.h
+++ b/libs/hwui/utils/TypeLogic.h
@@ -37,4 +37,4 @@
template <typename D, typename S> using same_cv = copy_cv<std::remove_cv_t<D>, S>;
template <typename D, typename S> using same_cv_t = typename same_cv<D, S>::type;
-}; // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
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/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index f60c338..8de2ab4 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -82,6 +82,11 @@
STATS_LOG_VALUE_TYPE type;
};
+struct WorkChain {
+ std::vector<int32_t> uids;
+ std::vector<std::string> tags;
+};
+
// Represents a parcelable object. Only used to send data from Android OS to statsd.
class StatsLogEventWrapper : public android::Parcelable {
public:
@@ -99,7 +104,9 @@
int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
- std::vector<StatsLogValue> getElements() const { return mElements; }
+ const std::vector<StatsLogValue>& getElements() const { return mElements; }
+
+ const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; }
private:
int mTagId;
@@ -109,6 +116,8 @@
int64_t mWallClockTimeNs;
std::vector<StatsLogValue> mElements;
+
+ std::vector<WorkChain> mWorkChains;
};
} // Namespace os
} // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..f6dfdef 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -58,6 +58,31 @@
ALOGE("statsd could not read wall clock time from parcel");
return res;
}
+ int numWorkChain = 0;
+ if ((res = in->readInt32(&numWorkChain)) != OK) {
+ ALOGE("statsd could not read number of work chains from parcel");
+ return res;
+ }
+ if (numWorkChain > 0) {
+ for (int i = 0; i < numWorkChain; i++) {
+ int numNodes = 0;
+ if ((res = in->readInt32(&numNodes)) != OK) {
+ ALOGE(
+ "statsd could not read number of nodes in work chain from parcel");
+ return res;
+ }
+ if (numNodes == 0) {
+ ALOGE("empty work chain");
+ return BAD_VALUE;
+ }
+ WorkChain wc;
+ for (int j = 0; j < numNodes; j++) {
+ wc.uids.push_back(in->readInt32());
+ wc.tags.push_back(std::string(String8(in->readString16()).string()));
+ }
+ mWorkChains.push_back(wc);
+ }
+ }
int dataSize = 0;
if ((res = in->readInt32(&dataSize)) != OK) {
ALOGE("statsd could not read data size from parcel");
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 7627cf6..180183e 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -26,7 +26,9 @@
oneway interface ILocationListener
{
void onLocationChanged(in Location location);
- void onStatusChanged(String provider, int status, in Bundle extras);
void onProviderEnabled(String provider);
void onProviderDisabled(String provider);
+
+ // --- deprecated ---
+ void onStatusChanged(String provider, int status, in Bundle extras);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index b5d835a..ae87998 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -99,9 +99,10 @@
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,
String opPackageName);
- void clearTestProviderStatus(String provider, String opPackageName);
boolean sendExtraCommand(String provider, String command, inout Bundle extras);
@@ -120,18 +121,4 @@
// used by gts tests to verify throttling whitelist
String[] getBackgroundThrottlingWhitelist();
-
- /**
- * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location
- * provider} to start the UI to modify the location permission for a package.
- *
- * <p>Can only be called by the location provider.
- *
- * @param packageName The package the permission belongs to
- * @param permission The (individual) permission to switch
- *
- * @return A pending intent that starts the permission management UI or {@code null} if the
- * intent cannot be created
- */
- PendingIntent createManageLocationPermissionIntent(in String packageName, in String permission);
}
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 88904c8..aa9dddc 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -44,29 +44,12 @@
void onLocationChanged(Location location);
/**
- * Called when the provider status changes. This method is called when
- * a provider is unable to fetch a location or if the provider has recently
- * become available after a period of unavailability.
+ * This callback will never be invoked and providers can be considers as always in the
+ * {@link LocationProvider#AVAILABLE} state.
*
- * @param provider the name of the location provider associated with this
- * update.
- * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
- * provider is out of service, and this is not expected to change in the
- * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
- * the provider is temporarily unavailable but is expected to be available
- * shortly; and {@link LocationProvider#AVAILABLE} if the
- * provider is currently available.
- * @param extras an optional Bundle which will contain provider specific
- * status variables.
- *
- * <p> A number of common key/value pairs for the extras Bundle are listed
- * below. Providers that use any of the keys on this list must
- * provide the corresponding value as described below.
- *
- * <ul>
- * <li> satellites - the number of satellites used to derive the fix
- * </ul>
+ * @deprecated This callback will never be invoked.
*/
+ @Deprecated
void onStatusChanged(String provider, int status, Bundle extras);
/**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 02680ab..3bf98b3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.Manifest;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -146,9 +145,14 @@
public static final String KEY_PROXIMITY_ENTERING = "entering";
/**
+ * This key is no longer in use.
+ *
* Key used for a Bundle extra holding an Integer status value
* when a status change is broadcast using a PendingIntent.
+ *
+ * @deprecated Status changes are deprecated and no longer broadcast.
*/
+ @Deprecated
public static final String KEY_STATUS_CHANGED = "status";
/**
@@ -1581,8 +1585,7 @@
}
/**
- * Sets mock status values for the given provider. These values will be used in place
- * of any actual values from the provider.
+ * This method has no effect as provider status has been deprecated and is no longer supported.
*
* @param provider the provider name
* @param status the mock status
@@ -1593,7 +1596,10 @@
* 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 method has no effect.
*/
+ @Deprecated
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
try {
mService.setTestProviderStatus(provider, status, extras, updateTime,
@@ -1604,21 +1610,19 @@
}
/**
- * Removes any mock status values associated with the given provider.
+ * This method has no effect as provider status has been deprecated and is no longer supported.
*
* @param provider the provider name
- *
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* 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 method has no effect.
*/
+ @Deprecated
public void clearTestProviderStatus(String provider) {
- try {
- mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L);
}
// --- GPS-specific support ---
@@ -2396,29 +2400,4 @@
return null;
}
}
-
- /**
- * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location
- * provider} to start the UI to modify the location permission for a package.
- *
- * <p>Can only be called by the location provider.
- *
- * @param packageName The package the permission belongs to
- * @param permission The (individual) location permission to switch
- *
- * @return A one-shot pending intent that starts the permission management UI or {@code null} if
- * the intent cannot be created
- *
- * @hide
- */
- @SystemApi
- public @Nullable PendingIntent createManageLocationPermissionIntent(@NonNull String packageName,
- @NonNull String permission) {
- try {
- return mService.createManageLocationPermissionIntent(packageName, permission);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
- }
- }
}
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index c4fd097..b69a9d7 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -34,8 +34,23 @@
* user-specified criteria.
*/
public class LocationProvider {
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int OUT_OF_SERVICE = 0;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int TEMPORARILY_UNAVAILABLE = 1;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int AVAILABLE = 2;
/**
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 1e69f16..d19559e 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,8 +11,8 @@
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 abstract int onGetStatus(android.os.Bundle);
- method public abstract long onGetStatusUpdateTime();
+ 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);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 30655f5..d45a4ba 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,14 +16,11 @@
package com.android.location.provider;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
+import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +33,10 @@
import com.android.internal.location.ProviderRequest;
import com.android.internal.util.FastPrintWriter;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+
/**
* Base class for location providers implemented as unbundled services.
*
@@ -173,6 +174,8 @@
}
/**
+ * This method will no longer be invoked.
+ *
* Returns a information on the status of this provider.
* <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
* out of service, and this is not expected to change in the near
@@ -183,10 +186,17 @@
*
* <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.
*/
- public abstract int onGetStatus(Bundle extras);
+ @Deprecated
+ public int onGetStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
/**
+ * This method will no longer be invoked.
+ *
* Returns the time at which the status was last updated. It is the
* responsibility of the provider to appropriately set this value using
* {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
@@ -195,8 +205,13 @@
* the same status again.
*
* @return time of last status update in millis since last reboot
+ *
+ * @deprecated This method will no longer be invoked.
*/
- public abstract long onGetStatusUpdateTime();
+ @Deprecated
+ public long onGetStatusUpdateTime() {
+ return 0;
+ }
/**
* Implements addditional location provider specific additional commands.
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/AudioManager.java b/media/java/android/media/AudioManager.java
index 976d380..d10900e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -541,6 +541,7 @@
* Adjusting the volume due to a hardware key press.
* @hide
*/
+ @SystemApi
public static final int FLAG_FROM_KEY = 1 << 12;
private static final String[] FLAG_NAMES = {
@@ -1911,7 +1912,7 @@
* system failed to generate a new session, a condition in which audio playback or recording
* will subsequently fail as well.
*/
- public static int generateAudioSessionId() {
+ public int generateAudioSessionId() {
int session = AudioSystem.newAudioSessionId();
if (session > 0) {
return session;
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 67cc456..082a375 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -75,6 +75,12 @@
*/
public static final int NUM_STREAMS = 5;
+ /** Maximum value for AudioTrack channel count
+ * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+ */
+ public static final int OUT_CHANNEL_COUNT_MAX = native_get_FCC_8();
+ private static native int native_get_FCC_8();
+
// Expose only the getter method publicly so we can change it in the future
private static final int NUM_STREAM_TYPES = 11;
@UnsupportedAppUsage
@@ -918,6 +924,15 @@
public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
+ /**
+ * Communicate UID of active assistant to audio policy service.
+ */
+ public static native int setAssistantUid(int uid);
+ /**
+ * Communicate UIDs of active accessibility services to audio policy service.
+ */
+ public static native int setA11yServicesUids(int[] uids);
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d37f8ab..2c4ec3a 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -93,11 +93,6 @@
*/
private static final float GAIN_MAX = 1.0f;
- /** Maximum value for AudioTrack channel count
- * @hide public for MediaCode only, do not un-hide or change to a numeric literal
- */
- public static final int CHANNEL_COUNT_MAX = native_get_FCC_8();
-
/** indicates AudioTrack state is stopped */
public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED
/** indicates AudioTrack state is paused */
@@ -1001,7 +996,8 @@
}
// mask of all the positional channels supported, however the allowed combinations
- // are further restricted by the matching left/right rule and CHANNEL_COUNT_MAX
+ // are further restricted by the matching left/right rule and
+ // AudioSystem.OUT_CHANNEL_COUNT_MAX
private static final int SUPPORTED_OUT_CHANNELS =
AudioFormat.CHANNEL_OUT_FRONT_LEFT |
AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
@@ -1124,7 +1120,7 @@
mChannelIndexMask = channelIndexMask;
if (mChannelIndexMask != 0) {
// restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
- final int indexMask = (1 << CHANNEL_COUNT_MAX) - 1;
+ final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
if ((channelIndexMask & ~indexMask) != 0) {
throw new IllegalArgumentException("Unsupported channel index configuration "
+ channelIndexMask);
@@ -1169,9 +1165,9 @@
return false;
}
final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
- if (channelCount > CHANNEL_COUNT_MAX) {
+ if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
loge("Channel configuration contains too many channels " +
- channelCount + ">" + CHANNEL_COUNT_MAX);
+ channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
return false;
}
// check for unsupported multichannel combinations:
@@ -2624,7 +2620,8 @@
* to the audio sink.
* <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
- * @param timestamp The timestamp of the first decodable audio frame in the provided audioData.
+ * @param timestamp The timestamp, in nanoseconds, of the first decodable audio frame in the
+ * provided audioData.
* @return zero or the positive number of bytes that were written, or one of the following
* error codes.
* <ul>
@@ -3418,7 +3415,6 @@
private native final int native_getRoutedDeviceId();
private native final void native_enableDeviceCallback();
private native final void native_disableDeviceCallback();
- static private native int native_get_FCC_8();
private native int native_applyVolumeShaper(
@NonNull VolumeShaper.Configuration configuration,
@@ -3427,6 +3423,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 a7e168f..cd36414 100644
--- a/media/java/android/media/CallbackDataSourceDesc.java
+++ b/media/java/android/media/CallbackDataSourceDesc.java
@@ -18,31 +18,27 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
-
/**
- * @hide
- * Structure for file data source descriptor.
+ * Structure of data source descriptor for sources using callback.
*
- * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)}
- * 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}.
*
*/
public class CallbackDataSourceDesc extends DataSourceDesc {
- private Media2DataSource mMedia2DataSource;
+ private DataSourceCallback mDataSourceCallback;
private CallbackDataSourceDesc() {
}
/**
- * Return the Media2DataSource of this data source.
- * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
- * @return the Media2DataSource of this data source
+ * Return the DataSourceCallback of this data source.
+ * @return the DataSourceCallback of this data source
*/
- public Media2DataSource getMedia2DataSource() {
- return mMedia2DataSource;
+ public DataSourceCallback getDataSourceCallback() {
+ return mDataSourceCallback;
}
/**
@@ -60,7 +56,7 @@
* </pre>
*/
public static class Builder extends BuilderBase<Builder> {
- private Media2DataSource mMedia2DataSource;
+ private DataSourceCallback mDataSourceCallback;
/**
* Constructs a new Builder with the defaults.
@@ -79,7 +75,7 @@
if (dsd == null) {
return; // use default
}
- mMedia2DataSource = dsd.mMedia2DataSource;
+ mDataSourceCallback = dsd.mDataSourceCallback;
}
/**
@@ -92,21 +88,21 @@
public @NonNull CallbackDataSourceDesc build() {
CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
super.build(dsd);
- dsd.mMedia2DataSource = mMedia2DataSource;
+ dsd.mDataSourceCallback = mDataSourceCallback;
return dsd;
}
/**
- * Sets the data source (Media2DataSource) to use.
+ * Sets the data source (DataSourceCallback) to use.
*
- * @param m2ds the Media2DataSource for the media to play
+ * @param dscb the DataSourceCallback for the media to play
* @return the same Builder instance.
- * @throws NullPointerException if m2ds is null.
+ * @throws NullPointerException if dscb is null.
*/
- public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) {
- Preconditions.checkNotNull(m2ds);
- mMedia2DataSource = m2ds;
+ public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) {
+ Media2Utils.checkArgument(dscb != null, "data source cannot be null.");
+ mDataSourceCallback = dscb;
return this;
}
}
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/Media2DataSource.java b/media/java/android/media/DataSourceCallback.java
similarity index 89%
rename from media/java/android/media/Media2DataSource.java
rename to media/java/android/media/DataSourceCallback.java
index 08df632..0d4d531 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -21,18 +21,17 @@
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.
*
* <p class="note">Methods of this interface may be called on multiple different
* threads. There will be a thread synchronization point between each call to ensure that
- * modifications to the state of your Media2DataSource are visible to future calls. This means
+ * modifications to the state of your DataSourceCallback are visible to future calls. This means
* you don't need to do your own synchronization unless you're modifying the
- * Media2DataSource from another thread while it's being used by the framework.</p>
+ * DataSourceCallback from another thread while it's being used by the framework.</p>
*
*/
-public abstract class Media2DataSource implements Closeable {
+public abstract class DataSourceCallback implements Closeable {
/**
* Called to request data from the given position.
*
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index aed3f84..7fc6f79 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -18,24 +18,24 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
-
/**
- * @hide
* Base class of data source descriptor.
*
- * Used by {@link MediaPlayer2#setDataSource(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}.
*
*/
public class DataSourceDesc {
// intentionally less than long.MAX_VALUE
- public static final long LONG_MAX = 0x7ffffffffffffffL;
+ static final long LONG_MAX = 0x7ffffffffffffffL;
// 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;
@@ -48,6 +48,19 @@
}
/**
+ * Releases the resources held by this {@code DataSourceDesc} object.
+ */
+ void close() {
+ }
+
+ // Have to declare protected for finalize() since it is protected
+ // in the base class Object.
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ }
+
+ /**
* Return the media Id of data source.
* @return the media Id of data source
*/
@@ -118,7 +131,7 @@
* @return the same instance of subclass of {@link DataSourceDesc}
*/
void build(@NonNull DataSourceDesc dsd) {
- Preconditions.checkNotNull(dsd);
+ Media2Utils.checkArgument(dsd != null, "dsd cannot be null.");
if (mStartPositionMs > mEndPositionMs) {
throw new IllegalStateException("Illegal start/end position: "
@@ -159,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/ExifInterface.java b/media/java/android/media/ExifInterface.java
index b96a585..32c4643 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -16,7 +16,10 @@
package android.media;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
@@ -26,12 +29,14 @@
import android.system.OsConstants;
import android.util.Log;
import android.util.Pair;
-import android.annotation.IntDef;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
import java.io.DataInput;
+import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
@@ -42,14 +47,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -58,11 +63,6 @@
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
/**
* This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
@@ -583,11 +583,19 @@
private static class ExifAttribute {
public final int format;
public final int numberOfComponents;
+ public final long bytesOffset;
public final byte[] bytes;
+ public static final long BYTES_OFFSET_UNKNOWN = -1;
+
private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
+ this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes);
+ }
+
+ private ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) {
this.format = format;
this.numberOfComponents = numberOfComponents;
+ this.bytesOffset = bytesOffset;
this.bytes = bytes;
}
@@ -1318,6 +1326,7 @@
private int mOrfThumbnailLength;
private int mRw2JpgFromRawOffset;
private boolean mIsSupportedFile;
+ private boolean mModified;
// Pattern to check non zero timestamp
private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -1328,7 +1337,14 @@
/**
* Reads Exif tags from the specified image file.
*/
- public ExifInterface(String filename) throws IOException {
+ public ExifInterface(@NonNull File file) throws IOException {
+ this(file.getAbsolutePath());
+ }
+
+ /**
+ * Reads Exif tags from the specified image file.
+ */
+ public ExifInterface(@NonNull String filename) throws IOException {
if (filename == null) {
throw new IllegalArgumentException("filename cannot be null");
}
@@ -1354,7 +1370,7 @@
* for writable and seekable file descriptors only. This constructor will not rewind the offset
* of the given file descriptor. Developers should close the file descriptor after use.
*/
- public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
+ public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
if (fileDescriptor == null) {
throw new IllegalArgumentException("fileDescriptor cannot be null");
}
@@ -1388,7 +1404,7 @@
* for input streams. The given input stream will proceed its current position. Developers
* should close the input stream after use.
*/
- public ExifInterface(InputStream inputStream) throws IOException {
+ public ExifInterface(@NonNull InputStream inputStream) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream cannot be null");
}
@@ -1414,7 +1430,7 @@
*
* @param tag the name of the tag.
*/
- private ExifAttribute getExifAttribute(String tag) {
+ private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) {
// Retrieves all tag groups. The value from primary image tag group has a higher priority
// than the value from the thumbnail tag group if there are more than one candidates.
for (int i = 0; i < EXIF_TAGS.length; ++i) {
@@ -1432,7 +1448,7 @@
*
* @param tag the name of the tag.
*/
- public String getAttribute(String tag) {
+ public @Nullable String getAttribute(@NonNull String tag) {
ExifAttribute attribute = getExifAttribute(tag);
if (attribute != null) {
if (!sTagSetForCompatibility.contains(tag)) {
@@ -1470,7 +1486,7 @@
* @param tag the name of the tag.
* @param defaultValue the value to return if the tag is not available.
*/
- public int getAttributeInt(String tag, int defaultValue) {
+ public int getAttributeInt(@NonNull String tag, int defaultValue) {
ExifAttribute exifAttribute = getExifAttribute(tag);
if (exifAttribute == null) {
return defaultValue;
@@ -1491,7 +1507,7 @@
* @param tag the name of the tag.
* @param defaultValue the value to return if the tag is not available.
*/
- public double getAttributeDouble(String tag, double defaultValue) {
+ public double getAttributeDouble(@NonNull String tag, double defaultValue) {
ExifAttribute exifAttribute = getExifAttribute(tag);
if (exifAttribute == null) {
return defaultValue;
@@ -1510,7 +1526,7 @@
* @param tag the name of the tag.
* @param value the value of the tag.
*/
- public void setAttribute(String tag, String value) {
+ public void setAttribute(@NonNull String tag, @Nullable String value) {
// Convert the given value to rational values for backwards compatibility.
if (value != null && sTagSetForCompatibility.contains(tag)) {
if (tag.equals(TAG_GPS_TIMESTAMP)) {
@@ -1772,12 +1788,18 @@
}
/**
- * Save the tag data into the original image file. This is expensive because it involves
- * copying all the data from one file to another and deleting the old file and renaming the
- * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
- * and make a single call rather than multiple calls for each attribute.
+ * Save the tag data into the original image file. This is expensive because
+ * it involves copying all the data from one file to another and deleting
+ * the old file and renaming the other. It's best to use
+ * {@link #setAttribute(String,String)} to set all attributes to write and
+ * make a single call rather than multiple calls for each attribute.
* <p>
* This method is only supported for JPEG files.
+ * <p class="note">
+ * Note: after calling this method, any attempts to obtain range information
+ * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
+ * will throw {@link IllegalStateException}, since the offsets may have
+ * changed in the newly written file.
* </p>
*/
public void saveAttributes() throws IOException {
@@ -1789,6 +1811,10 @@
"ExifInterface does not support saving attributes for the current input.");
}
+ // Remember the fact that we've changed the file on disk from what was
+ // originally parsed, meaning we can't answer range questions
+ mModified = true;
+
// Keep the thumbnail in memory
mThumbnailBytes = getThumbnail();
@@ -1849,6 +1875,15 @@
}
/**
+ * Returns true if the image file has the given attribute defined.
+ *
+ * @param tag the name of the tag.
+ */
+ public boolean hasAttribute(String tag) {
+ return (getExifAttribute(tag) != null);
+ }
+
+ /**
* Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
* JPEG compressed thumbnail.
* The returned data can be decoded using
@@ -1968,17 +2003,45 @@
*
* @return two-element array, the offset in the first value, and length in
* the second, or {@code null} if no thumbnail was found.
+ * @throws IllegalStateException if {@link #saveAttributes()} has been
+ * called since the underlying file was initially parsed, since
+ * that means offsets may have changed.
*/
- public long[] getThumbnailRange() {
- if (!mHasThumbnail) {
- return null;
+ public @Nullable long[] getThumbnailRange() {
+ if (mModified) {
+ throw new IllegalStateException(
+ "The underlying file has been modified since being parsed");
}
- long[] range = new long[2];
- range[0] = mThumbnailOffset;
- range[1] = mThumbnailLength;
+ if (mHasThumbnail) {
+ return new long[] { mThumbnailOffset, mThumbnailLength };
+ } else {
+ return null;
+ }
+ }
- return range;
+ /**
+ * Returns the offset and length of the requested tag inside the image file,
+ * or {@code null} if the tag is not contained.
+ *
+ * @return two-element array, the offset in the first value, and length in
+ * the second, or {@code null} if no tag was found.
+ * @throws IllegalStateException if {@link #saveAttributes()} has been
+ * called since the underlying file was initially parsed, since
+ * that means offsets may have changed.
+ */
+ public @Nullable long[] getAttributeRange(@NonNull String tag) {
+ if (mModified) {
+ throw new IllegalStateException(
+ "The underlying file has been modified since being parsed");
+ }
+
+ final ExifAttribute attribute = getExifAttribute(tag);
+ if (attribute != null) {
+ return new long[] { attribute.bytesOffset, attribute.bytes.length };
+ } else {
+ return null;
+ }
}
/**
@@ -2023,13 +2086,41 @@
}
/**
- * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
- * Returns -1 if the date time information if not available.
+ * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
+ *
* @hide
*/
@UnsupportedAppUsage
- public long getDateTime() {
- String dateTimeString = getAttribute(TAG_DATETIME);
+ public @CurrentTimeMillisLong long getDateTime() {
+ return parseDateTime(getAttribute(TAG_DATETIME),
+ getAttribute(TAG_SUBSEC_TIME));
+ }
+
+ /**
+ * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
+ * invalid.
+ *
+ * @hide
+ */
+ public @CurrentTimeMillisLong long getDateTimeDigitized() {
+ return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
+ getAttribute(TAG_SUBSEC_TIME_DIGITIZED));
+ }
+
+ /**
+ * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
+ * invalid.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public @CurrentTimeMillisLong long getDateTimeOriginal() {
+ return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
+ getAttribute(TAG_SUBSEC_TIME_ORIGINAL));
+ }
+
+ private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString,
+ @Nullable String subSecs) {
if (dateTimeString == null
|| !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
@@ -2041,7 +2132,6 @@
if (datetime == null) return -1;
long msecs = datetime.getTime();
- String subSecs = getAttribute(TAG_SUBSEC_TIME);
if (subSecs != null) {
try {
long sub = Long.parseLong(subSecs);
@@ -2545,13 +2635,18 @@
if (size == 0) {
return 0;
}
- // We don't allow read positions after the available bytes,
- // the input stream won't be able to seek back then.
- if (position < 0 || position >= in.available()) {
+ if (position < 0) {
return -1;
}
try {
if (mPosition != position) {
+ // We don't allow seek to positions after the available bytes,
+ // the input stream won't be able to seek back then.
+ // However, if we hit an exception before (mPosition set to -1),
+ // let it try the seek in hope it might recover.
+ if (mPosition >= 0 && position >= mPosition + in.available()) {
+ return -1;
+ }
in.seek(position);
mPosition = position;
}
@@ -2559,8 +2654,8 @@
// If the read will cause us to go over the available bytes,
// reduce the size so that we stay in the available range.
// Otherwise the input stream may not be able to seek back.
- if (mPosition + size > in.available()) {
- size = in.available() - (int)mPosition;
+ if (size > in.available()) {
+ size = in.available();
}
int bytesRead = in.read(buffer, offset, size);
@@ -3125,9 +3220,11 @@
continue;
}
- byte[] bytes = new byte[(int) byteCount];
+ final int bytesOffset = dataInputStream.peek();
+ final byte[] bytes = new byte[(int) byteCount];
dataInputStream.readFully(bytes);
- ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
+ ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
+ bytesOffset, bytes);
mAttributes[ifdType].put(tag.name, attribute);
// DNG files have a DNG Version tag specifying the version of specifications that the
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index 5d8ff49..aca8dbe 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -17,22 +17,23 @@
package android.media;
import android.annotation.NonNull;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
+import java.io.IOException;
/**
- * @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using file descriptor.
*
- * Used by {@link MediaPlayer2#setDataSource(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}.
*
*/
public class FileDataSourceDesc extends DataSourceDesc {
+ private static final String TAG = "FileDataSourceDesc";
+
/**
* Used when the length of file descriptor is unknown.
*
@@ -40,34 +41,61 @@
*/
public static final long FD_LENGTH_UNKNOWN = LONG_MAX;
- private FileDescriptor mFD;
+ private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
private FileDataSourceDesc() {
+ super();
}
/**
- * Return the FileDescriptor of this data source.
- * @return the FileDescriptor of this data source
+ * Releases the resources held by this {@code FileDataSourceDesc} object.
*/
- public FileDescriptor getFileDescriptor() {
- return mFD;
+ @Override
+ void close() {
+ super.close();
+ closeFD();
}
/**
- * Return the offset associated with the FileDescriptor of this data source.
+ * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+ */
+ void closeFD() {
+ synchronized (this) {
+ if (mPFD != null) {
+ try {
+ mPFD.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close pfd: " + e);
+ }
+
+ mPFD = null;
+ }
+ }
+ }
+
+ /**
+ * Return the ParcelFileDescriptor of this data source.
+ * @return the ParcelFileDescriptor of this data source
+ */
+ public ParcelFileDescriptor getParcelFileDescriptor() {
+ return mPFD;
+ }
+
+ /**
+ * Return the offset associated with the ParcelFileDescriptor of this data source.
* It's meaningful only when it has been set by the {@link Builder}.
- * @return the offset associated with the FileDescriptor of this data source
+ * @return the offset associated with the ParcelFileDescriptor of this data source
*/
public long getOffset() {
return mOffset;
}
/**
- * Return the content length associated with the FileDescriptor of this data source.
+ * Return the content length associated with the ParcelFileDescriptor of this data source.
* {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
- * @return the content length associated with the FileDescriptor of this data source
+ * @return the content length associated with the ParcelFileDescriptor of this data source
*/
public long getLength() {
return mLength;
@@ -80,7 +108,7 @@
*
* <pre class="prettyprint">
* FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder()
- * .setDataSource(fd, 0, srcLength)
+ * .setDataSource(pfd, 0, srcLength)
* .setStartPosition(1000)
* .setEndPosition(15000)
* .build();
@@ -88,7 +116,7 @@
* </pre>
*/
public static class Builder extends BuilderBase<Builder> {
- private FileDescriptor mFD;
+ private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
@@ -109,7 +137,7 @@
if (dsd == null) {
return; // use default
}
- mFD = dsd.mFD;
+ mPFD = dsd.mPFD;
mOffset = dsd.mOffset;
mLength = dsd.mLength;
}
@@ -124,7 +152,7 @@
public @NonNull FileDataSourceDesc build() {
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
- dsd.mFD = mFD;
+ dsd.mPFD = mPFD;
dsd.mOffset = mOffset;
dsd.mLength = mLength;
@@ -132,38 +160,46 @@
}
/**
- * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
- * to close the file descriptor after the source has been used.
+ * 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},
+ * {@link MediaPlayer2#setNextDataSource} or
+ * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
+ * close the ParcelFileDescriptor.
*
- * @param fd the FileDescriptor for the file to play
+ * @param pfd the ParcelFileDescriptor for the file to play
* @return the same Builder instance.
- * @throws NullPointerException if fd is null.
+ * @throws NullPointerException if pfd is null.
*/
- public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) {
- Preconditions.checkNotNull(fd);
+ public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
+ Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
resetDataSource();
- mFD = fd;
+ mPFD = pfd;
return this;
}
/**
- * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
- * to close the file descriptor after the source has been used.
+ * 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},
+ * {@link MediaPlayer2#setNextDataSource} or
+ * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
+ * close the ParcelFileDescriptor.
*
* Any negative number for offset is treated as 0.
* Any negative number for length is treated as maximum length of the data source.
*
- * @param fd the FileDescriptor for the file to play
+ * @param pfd the ParcelFileDescriptor for the file to play
* @param offset the offset into the file where the data to be played starts, in bytes
* @param length the length in bytes of the data to be played
* @return the same Builder instance.
- * @throws NullPointerException if fd is null.
+ * @throws NullPointerException if pfd is null.
*/
public @NonNull Builder setDataSource(
- @NonNull FileDescriptor fd, long offset, long length) {
- Preconditions.checkNotNull(fd);
+ @NonNull ParcelFileDescriptor pfd, long offset, long length) {
+ Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
if (offset < 0) {
offset = 0;
}
@@ -171,14 +207,14 @@
length = FD_LENGTH_UNKNOWN;
}
resetDataSource();
- mFD = fd;
+ mPFD = pfd;
mOffset = offset;
mLength = length;
return this;
}
private void resetDataSource() {
- mFD = null;
+ mPFD = null;
mOffset = 0;
mLength = FD_LENGTH_UNKNOWN;
}
diff --git a/media/java/android/media/Media2HTTPConnection.java b/media/java/android/media/Media2HTTPConnection.java
index 0d7825a..a369a62 100644
--- a/media/java/android/media/Media2HTTPConnection.java
+++ b/media/java/android/media/Media2HTTPConnection.java
@@ -16,27 +16,27 @@
package android.media;
-import android.net.NetworkUtils;
+import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
import android.os.StrictMode;
import android.util.Log;
import java.io.BufferedInputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
import java.net.HttpURLConnection;
+import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.UnknownHostException;
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.Map;
-import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
/** @hide */
public class Media2HTTPConnection {
private static final String TAG = "Media2HTTPConnection";
@@ -161,10 +161,10 @@
if (host.equalsIgnoreCase("localhost")) {
return true;
}
- if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
+ if (InetAddress.getByName(host).isLoopbackAddress()) {
return true;
}
- } catch (IllegalArgumentException iex) {
+ } catch (IllegalArgumentException | UnknownHostException e) {
}
return false;
}
diff --git a/media/java/android/media/Media2Utils.java b/media/java/android/media/Media2Utils.java
index 066233d..5fd6191 100644
--- a/media/java/android/media/Media2Utils.java
+++ b/media/java/android/media/Media2Utils.java
@@ -31,6 +31,20 @@
private Media2Utils() {
}
+ /**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(boolean expression, String errorMessage) {
+ if (!expression) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
public static synchronized void storeCookies(List<HttpCookie> cookies) {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler == null) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 995ebb2..6301993 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -16,6 +16,9 @@
package android.media;
+import static android.media.Utils.intersectSortedDistinctRanges;
+import static android.media.Utils.sortDistinctRanges;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
@@ -32,9 +35,6 @@
import java.util.Map;
import java.util.Set;
-import static android.media.Utils.intersectSortedDistinctRanges;
-import static android.media.Utils.sortDistinctRanges;
-
/**
* Provides information about a given media codec available on the device. You can
* iterate through all codecs available by querying {@link MediaCodecList}. For example,
@@ -1117,7 +1117,7 @@
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
sampleRateRange = Range.create(1, 96000);
bitRates = Range.create(1, 10000000);
- maxChannels = AudioTrack.CHANNEL_COUNT_MAX;
+ maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
sampleRateRange = Range.create(1, 655350);
// lossless codec, so bitrate is ignored
@@ -1135,6 +1135,10 @@
maxChannels = 6;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
+ sampleRates = new int[] { 44100, 48000, 96000, 192000 };
+ bitRates = Range.create(16000, 2688000);
+ maxChannels = 24;
} else {
Log.w(TAG, "Unsupported mime " + mime);
mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index c203fa9..7785900 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -434,9 +434,12 @@
*/
@NonNull
public List<AudioPresentation> getAudioPresentations(int trackIndex) {
- return new ArrayList<AudioPresentation>();
+ return native_getAudioPresentations(trackIndex);
}
+ @NonNull
+ private native List<AudioPresentation> native_getAudioPresentations(int trackIndex);
+
/**
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5dee16e..b7743c9 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_AC4 = "audio/ac4";
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
/**
@@ -937,7 +947,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>
@@ -949,7 +958,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;
}
@@ -968,11 +977,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.
@@ -988,64 +1044,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");
@@ -1056,39 +1213,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}).
*
@@ -1101,7 +1458,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);
}
@@ -1111,8 +1468,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();
@@ -1131,8 +1488,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);
@@ -1147,8 +1504,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();
@@ -1160,7 +1517,7 @@
}
@Override
- public String toString() {
+ public @NonNull String toString() {
return mMap.toString();
}
}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 8288976..4805780 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -20,14 +20,13 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
+import android.annotation.StringDef;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer2.DrmInfo;
import android.media.MediaPlayer2Proto.PlayerMessage;
import android.media.MediaPlayer2Proto.Value;
import android.net.Uri;
@@ -35,6 +34,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.util.Log;
@@ -45,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;
@@ -70,14 +68,16 @@
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
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:
@@ -98,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>.
@@ -134,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
@@ -161,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>
@@ -180,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>
*
@@ -208,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>
*
@@ -248,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
@@ -298,21 +296,7 @@
private volatile float mVolume = 1.0f;
private VideoSize mVideoSize = new VideoSize(0, 0);
- // TODO: create per-source drm fields in SourceInfo
- // Modular DRM
- private final Object mDrmLock = new Object();
- //--- guarded by |mDrmLock| start
- private UUID mDrmUUID;
- private DrmInfo mDrmInfo;
- private MediaDrm mDrmObj;
- private byte[] mDrmSessionId;
- private boolean mDrmInfoResolved;
- private boolean mActiveDrmScheme;
- private boolean mDrmConfigAllowed;
- private boolean mDrmProvisioningInProgress;
- private boolean mPrepareDrmInProgress;
- private ProvisioningThread mDrmProvisioningThread;
- //--- guarded by |mDrmLock| end
+ private ExecutorService mDrmThreadPool = Executors.newCachedThreadPool();
// Creating a dummy audio track, used for keeping session id alive
private final Object mSessionIdLock = new Object();
@@ -326,6 +310,7 @@
private final List<Task> mPendingTasks = new LinkedList<>();
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ private final AtomicLong mTaskIdGenerator = new AtomicLong(0);
@GuardedBy("mTaskLock")
boolean mIsPreviousCommandSeekTo = false;
@@ -411,12 +396,13 @@
mHandlerThread = null;
}
+ clearSourceInfos();
+
// Modular DRM clean up
mOnDrmConfigHelper = null;
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- resetDrmState();
native_release();
@@ -456,15 +442,8 @@
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- synchronized (mSrcLock) {
- mCurrentSourceInfo = null;
- mNextSourceInfos.clear();
- }
- synchronized (mTaskLock) {
- mPendingTasks.clear();
- mIsPreviousCommandSeekTo = false;
- }
+ clearSourceInfos();
stayAwake(false);
native_reset();
@@ -478,7 +457,6 @@
mTaskHandler.removeCallbacksAndMessages(null);
}
- resetDrmState();
}
private native void native_reset();
@@ -684,6 +662,8 @@
/**
* Sets the data source as described by a DataSourceDesc.
+ * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+ * in the {@link FileDataSourceDesc} will be closed by the player.
*
* @param dsd the descriptor of data source you want to play
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -693,23 +673,30 @@
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
int state = getState();
- if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
- throw new IllegalStateException("called in wrong state " + state);
- }
+ try {
+ if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
+ throw new IllegalStateException("called in wrong state " + state);
+ }
- synchronized (mSrcLock) {
- mCurrentSourceInfo = new SourceInfo(dsd);
- handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+ synchronized (mSrcLock) {
+ setCurrentSourceInfo_l(new SourceInfo(dsd));
+ handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+ }
+ } finally {
+ dsd.close();
}
}
+
});
}
/**
* Sets a single data source as described by a DataSourceDesc which will be played
* after current data source is finished.
+ * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+ * in the {@link FileDataSourceDesc} will be closed by the player.
*
* @param dsd the descriptor of data source you want to play after current one
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -719,9 +706,9 @@
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
synchronized (mSrcLock) {
- mNextSourceInfos.clear();
+ clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
}
prepareNextDataSource();
@@ -731,6 +718,8 @@
/**
* Sets a list of data sources to be played sequentially after current data source is done.
+ * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+ * in the {@link FileDataSourceDesc} will be closed by the player.
*
* @param dsds the list of data sources you want to play after current one
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -743,17 +732,15 @@
if (dsds == null || dsds.size() == 0) {
throw new IllegalArgumentException("data source list cannot be null or empty.");
}
- for (DataSourceDesc dsd : dsds) {
- if (dsd == null) {
- throw new IllegalArgumentException(
- "DataSourceDesc in the source list cannot be null.");
- }
- }
synchronized (mSrcLock) {
- mNextSourceInfos.clear();
+ clearNextSourceInfos_l();
for (DataSourceDesc dsd : dsds) {
- mNextSourceInfos.add(new SourceInfo(dsd));
+ if (dsd != null) {
+ mNextSourceInfos.add(new SourceInfo(dsd));
+ } else {
+ Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
+ }
}
}
prepareNextDataSource();
@@ -770,7 +757,9 @@
return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
@Override
void process() {
- mNextSourceInfos.clear();
+ synchronized (mSrcLock) {
+ clearNextSourceInfos_l();
+ }
}
});
}
@@ -780,7 +769,7 @@
*
* @return the current DataSourceDesc
*/
- public DataSourceDesc getCurrentDataSource() {
+ public @Nullable DataSourceDesc getCurrentDataSource() {
synchronized (mSrcLock) {
return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD;
}
@@ -788,20 +777,20 @@
private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
throws IOException {
- checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
if (dsd instanceof CallbackDataSourceDesc) {
CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd;
handleDataSource(isCurrent,
srcId,
- cbDSD.getMedia2DataSource(),
+ cbDSD.getDataSourceCallback(),
cbDSD.getStartPosition(),
cbDSD.getEndPosition());
} else if (dsd instanceof FileDataSourceDesc) {
FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
handleDataSource(isCurrent,
srcId,
- fileDSD.getFileDescriptor(),
+ fileDSD.getParcelFileDescriptor(),
fileDSD.getOffset(),
fileDSD.getLength(),
fileDSD.getStartPosition(),
@@ -885,7 +874,7 @@
if (afd.getDeclaredLength() < 0) {
handleDataSource(isCurrent,
srcId,
- afd.getFileDescriptor(),
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()),
0,
DataSourceDesc.LONG_MAX,
startPos,
@@ -893,7 +882,7 @@
} else {
handleDataSource(isCurrent,
srcId,
- afd.getFileDescriptor(),
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()),
afd.getStartOffset(),
afd.getDeclaredLength(),
startPos,
@@ -959,7 +948,8 @@
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
- handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
+ handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd),
+ 0, DataSourceDesc.LONG_MAX, startPos, endPos);
is.close();
} else {
throw new IOException("handleDataSource failed.");
@@ -983,9 +973,10 @@
*/
private void handleDataSource(
boolean isCurrent, long srcId,
- FileDescriptor fd, long offset, long length,
+ ParcelFileDescriptor pfd, long offset, long length,
long startPos, long endPos) throws IOException {
- nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
+ nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length,
+ startPos, endPos);
}
private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
@@ -994,15 +985,15 @@
/**
* @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
+ * @throws IllegalArgumentException if dataSource is not a valid DataSourceCallback
*/
- private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource,
+ private void handleDataSource(boolean isCurrent, long srcId, DataSourceCallback dataSource,
long startPos, long endPos) {
nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
}
private native void nativeHandleDataSourceCallback(
- boolean isCurrent, long srcId, Media2DataSource dataSource,
+ boolean isCurrent, long srcId, DataSourceCallback dataSource,
long startPos, long endPos);
// return true if there is a next data source, false otherwise.
@@ -1036,7 +1027,10 @@
MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
mTaskHandler.handleMessage(msg, nextSource.mId);
- mNextSourceInfos.poll();
+ SourceInfo nextSourceInfo = mNextSourceInfos.poll();
+ if (nextSource != null) {
+ nextSourceInfo.close();
+ }
return prepareNextDataSource();
}
}
@@ -1057,7 +1051,7 @@
SourceInfo nextSourceInfo = mNextSourceInfos.peek();
if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
// Switch to next source only when it has been prepared.
- mCurrentSourceInfo = mNextSourceInfos.poll();
+ setCurrentSourceInfo_l(mNextSourceInfos.poll());
long srcId = mCurrentSourceInfo.mId;
try {
@@ -1251,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;
@@ -1273,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();
- }
}
});
}
@@ -1302,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.
*
@@ -1349,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);
}
@@ -1459,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.
*/
@@ -1513,7 +1497,7 @@
return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
- checkArgument(params != null, "the BufferingParams cannot be null");
+ Media2Utils.checkArgument(params != null, "the BufferingParams cannot be null");
native_setBufferingParams(params);
}
});
@@ -1536,7 +1520,7 @@
return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@Override
void process() {
- checkArgument(params != null, "the PlaybackParams cannot be null");
+ Media2Utils.checkArgument(params != null, "the PlaybackParams cannot be null");
native_setPlaybackParams(params);
}
});
@@ -1564,7 +1548,7 @@
return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@Override
void process() {
- checkArgument(params != null, "the SyncParams cannot be null");
+ Media2Utils.checkArgument(params != null, "the SyncParams cannot be null");
native_setSyncParams(params);
}
});
@@ -1890,7 +1874,6 @@
* Gets the track type.
* @return TrackType which indicates if the track is video, audio, timed text.
*/
- @UnsupportedAppUsage
public int getTrackType() {
return mTrackType;
}
@@ -1901,7 +1884,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;
@@ -1932,19 +1914,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 */
@@ -1989,9 +1972,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 {
@@ -2009,7 +1992,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;
}
@@ -2157,7 +2140,7 @@
final int what = msg.arg1;
final int extra = msg.arg2;
- final SourceInfo sourceInfo = getSourceInfoById(srcId);
+ final SourceInfo sourceInfo = getSourceInfo(srcId);
if (sourceInfo == null) {
return;
}
@@ -2211,11 +2194,11 @@
Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
} else if (msg.obj instanceof byte[]) {
// The PlayerMessage was parsed already in postEventFromNative
- final DrmInfo drmInfo;
- synchronized (mDrmLock) {
- if (mDrmInfo != null) {
- drmInfo = mDrmInfo.makeCopy();
+ final DrmInfo drmInfo;
+ synchronized (sourceInfo) {
+ if (sourceInfo.mDrmInfo != null) {
+ drmInfo = sourceInfo.mDrmInfo.makeCopy();
} else {
drmInfo = null;
}
@@ -2287,7 +2270,7 @@
}
});
- SourceInfo src = getSourceInfoById(srcId);
+ SourceInfo src = getSourceInfo(srcId);
if (src != null) {
src.mBufferedPercentage.set(percent);
}
@@ -2488,6 +2471,7 @@
return;
}
+ final SourceInfo sourceInfo = mp.getSourceInfo(srcId);
switch (what) {
case MEDIA_DRM_INFO:
// We need to derive mDrmInfo before prepare() returns so processing it here
@@ -2495,7 +2479,7 @@
// notification looper so its handleMessage might process the event after prepare()
// has returned.
Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
- if (obj != null) {
+ if (obj != null && sourceInfo != null) {
PlayerMessage playerMsg;
try {
playerMsg = PlayerMessage.parseFrom(obj);
@@ -2503,12 +2487,13 @@
Log.w(TAG, "MEDIA_DRM_INFO failed to parse msg.obj " + obj);
break;
}
- DrmInfo drmInfo = new DrmInfo(playerMsg);
- synchronized (mp.mDrmLock) {
- mp.mDrmInfo = drmInfo;
+ DrmInfo drmInfo = DrmInfo.create(playerMsg);
+ synchronized (sourceInfo) {
+ sourceInfo.mDrmInfo = drmInfo;
}
} else {
- Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);
+ Log.w(TAG, "MEDIA_DRM_INFO sourceInfo " + sourceInfo
+ + " msg.obj of unexpected type " + obj);
}
break;
@@ -2517,8 +2502,10 @@
// mainly for prepare() use case. For prepare(), this still can run to a race
// condition b/c MediaPlayerNative releases the prepare() lock before calling notify
// so we also set mDrmInfoResolved in prepare().
- synchronized (mp.mDrmLock) {
- mp.mDrmInfoResolved = true;
+ if (sourceInfo != null) {
+ synchronized (sourceInfo) {
+ sourceInfo.mDrmInfoResolved = true;
+ }
}
break;
}
@@ -2551,7 +2538,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
@@ -2562,7 +2549,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
@@ -2583,7 +2571,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.
@@ -2595,7 +2584,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.
@@ -2606,7 +2596,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.
@@ -2617,7 +2609,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) { }
/**
@@ -2628,7 +2620,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.
@@ -2637,7 +2630,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.
@@ -2646,7 +2639,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();
@@ -2690,12 +2684,6 @@
}
}
- private static void checkArgument(boolean expression, String errorMessage) {
- if (!expression) {
- throw new IllegalArgumentException(errorMessage);
- }
- }
-
private void sendEvent(final EventNotifier notifier) {
synchronized (mEventCbLock) {
try {
@@ -3047,10 +3035,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
@@ -3070,7 +3058,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
*/
@@ -3103,7 +3091,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,
@@ -3148,9 +3136,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;
/**
@@ -3180,6 +3169,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 {
/**
@@ -3195,15 +3185,14 @@
* 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) {
- synchronized (mDrmLock) {
- mOnDrmConfigHelper = listener;
- }
+ mOnDrmConfigHelper = listener;
}
private OnDrmConfigHelper mOnDrmConfigHelper;
@@ -3211,6 +3200,7 @@
/**
* Interface definition for callbacks to be invoked when the player has the corresponding
* DRM events.
+ * @hide
*/
public static class DrmEventCallback {
/**
@@ -3244,6 +3234,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,
@@ -3264,6 +3255,7 @@
* Unregisters the {@link DrmEventCallback}.
*
* @param eventCallback the callback to be unregistered
+ * @hide
*/
// This is a synchronous call.
public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
@@ -3281,31 +3273,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;
@@ -3321,32 +3319,55 @@
@Retention(RetentionPolicy.SOURCE)
public @interface PrepareDrmStatusCode {}
+ /** @hide */
+ @IntDef({
+ MediaDrm.KEY_TYPE_STREAMING,
+ MediaDrm.KEY_TYPE_OFFLINE,
+ MediaDrm.KEY_TYPE_RELEASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaDrmKeyType {}
+
+ /** @hide */
+ @StringDef({
+ MediaDrm.PROPERTY_VENDOR,
+ MediaDrm.PROPERTY_VERSION,
+ MediaDrm.PROPERTY_DESCRIPTION,
+ MediaDrm.PROPERTY_ALGORITHMS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaDrmStringProperty {}
+
/**
* Retrieves the DRM Info associated with the given source
*
* @param dsd The DRM protected data source
*
* @throws IllegalStateException if called before being prepared
+ * @hide
*/
public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
- // TODO: this implementation only works when dsd is the only data source
- DrmInfo drmInfo = null;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ DrmInfo drmInfo = null;
- // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet;
- // regardless below returns drmInfo anyway instead of raising an exception
- synchronized (mDrmLock) {
- if (!mDrmInfoResolved && mDrmInfo == null) {
- final String msg = "The Player has not been prepared yet";
- Log.v(TAG, msg);
- throw new IllegalStateException(msg);
- }
+ // there is not much point if the app calls getDrmInfo within an OnDrmInfoListener;
+ // regardless below returns drmInfo anyway instead of raising an exception
+ synchronized (sourceInfo) {
+ if (!sourceInfo.mDrmInfoResolved && sourceInfo.mDrmInfo == null) {
+ final String msg = "The Player has not been prepared yet";
+ Log.v(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
- if (mDrmInfo != null) {
- drmInfo = mDrmInfo.makeCopy();
- }
- } // synchronized
+ if (sourceInfo.mDrmInfo != null) {
+ drmInfo = sourceInfo.mDrmInfo.makeCopy();
+ }
+ } // synchronized
- return drmInfo;
+ return drmInfo;
+ }
+ return null;
}
/**
@@ -3379,18 +3400,32 @@
* {@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) {
- // TODO: this implementation only works when dsd is the only data source
return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
@Override
void process() {
- int status = PREPARE_DRM_STATUS_SUCCESS;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
boolean sendEvent = true;
+ if (sourceInfo == null) {
+ Log.e(TAG, "prepareDrm(): DataSource not found.");
+ } else if (sourceInfo.mDrmInfo == null) {
+ // only allowing if tied to a protected source;
+ // might relax for releasing offline keys
+ Log.e(TAG, "prepareDrm(): Wrong usage: The player must be prepared and "
+ + "DRM info be retrieved before this call.");
+ } else {
+ status = PREPARE_DRM_STATUS_SUCCESS;
+ }
+
try {
- doPrepareDrm(dsd, uuid);
+ if (status == PREPARE_DRM_STATUS_SUCCESS) {
+ sourceInfo.mDrmHandle.prepare(uuid);
+ }
} catch (ResourceBusyException e) {
status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
} catch (UnsupportedSchemeException e) {
@@ -3399,14 +3434,14 @@
Log.w(TAG, "prepareDrm: NotProvisionedException");
// handle provisioning internally; it'll reset mPrepareDrmInProgress
- status = handleProvisioninig(dsd, uuid);
+ status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId);
if (status == PREPARE_DRM_STATUS_SUCCESS) {
// DrmEventCallback will be fired in provisioning
sendEvent = false;
} else {
- synchronized (mDrmLock) {
- cleanDrmObj();
+ synchronized (sourceInfo.mDrmHandle) {
+ sourceInfo.mDrmHandle.cleanDrmObj();
}
switch (status) {
@@ -3449,95 +3484,6 @@
});
}
- private void doPrepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid)
- throws UnsupportedSchemeException, ResourceBusyException,
- NotProvisionedException {
- Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
-
- synchronized (mDrmLock) {
- // only allowing if tied to a protected source; might relax for releasing offline keys
- if (mDrmInfo == null) {
- final String msg = "prepareDrm(): Wrong usage: The player must be prepared and "
- + "DRM info be retrieved before this call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mActiveDrmScheme) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "an active DRM scheme with " + mDrmUUID;
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mPrepareDrmInProgress) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "a pending prepareDrm call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mDrmProvisioningInProgress) {
- final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- // shouldn't need this; just for safeguard
- cleanDrmObj();
-
- mPrepareDrmInProgress = true;
-
- try {
- // only creating the DRM object to allow pre-openSession configuration
- prepareDrm_createDrmStep(uuid);
- } catch (Exception e) {
- Log.w(TAG, "prepareDrm(): Exception ", e);
- mPrepareDrmInProgress = false;
- throw e;
- }
-
- mDrmConfigAllowed = true;
- } // synchronized
-
- // call the callback outside the lock
- if (mOnDrmConfigHelper != null) {
- mOnDrmConfigHelper.onDrmConfig(this, dsd);
- }
-
- synchronized (mDrmLock) {
- mDrmConfigAllowed = false;
- boolean earlyExit = false;
-
- try {
- prepareDrm_openSessionStep(uuid);
-
- mDrmUUID = uuid;
- mActiveDrmScheme = true;
- mPrepareDrmInProgress = false;
- } catch (IllegalStateException e) {
- final String msg = "prepareDrm(): Wrong usage: The player must be "
- + "in the prepared state to call prepareDrm().";
- Log.e(TAG, msg);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw new IllegalStateException(msg);
- } catch (NotProvisionedException e) {
- Log.w(TAG, "prepareDrm: NotProvisionedException", e);
- throw e;
- } catch (Exception e) {
- Log.e(TAG, "prepareDrm: Exception " + e);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw e;
- } finally {
- if (earlyExit) { // clean up object if didn't succeed
- cleanDrmObj();
- }
- } // finally
- } // synchronized
- }
-
/**
* Releases the DRM session for the given data source
* <p>
@@ -3548,42 +3494,18 @@
* @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)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- synchronized (mDrmLock) {
- Log.v(TAG, "releaseDrm:");
-
- if (!mActiveDrmScheme) {
- Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
- throw new NoDrmSchemeException(
- "releaseDrm: No active DRM scheme to release.");
- }
-
- try {
- // we don't have the player's state in this layer. The below call raises
- // exception if we're in a non-stopped/prepared state.
-
- // for cleaning native/mediaserver crypto object
- native_releaseDrm();
-
- // for cleaning client-side MediaDrm object; only called if above has succeeded
- cleanDrmObj();
-
- mActiveDrmScheme = false;
- } catch (IllegalStateException e) {
- Log.w(TAG, "releaseDrm: Exception ", e);
- throw new IllegalStateException(
- "releaseDrm: The player is not in a valid state.");
- } catch (Exception e) {
- Log.e(TAG, "releaseDrm: Exception ", e);
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.release();
+ }
}
- 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
@@ -3623,52 +3545,24 @@
* This may be {@code null} if no additional parameters are to be sent.
*
* @throws NoDrmSchemeException if there is no active DRM session
+ * @hide
*/
- @NonNull
public MediaDrm.KeyRequest getDrmKeyRequest(
@NonNull DataSourceDesc dsd,
@Nullable byte[] keySetId, @Nullable byte[] initData,
- @Nullable String mimeType, @MediaDrm.KeyType int keyType,
+ @Nullable String mimeType, @MediaDrmKeyType int keyType,
@Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- Log.v(TAG, "getDrmKeyRequest: "
- + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType
- + " keyType: " + keyType + " optionalParameters: " + optionalParameters);
+ Log.v(TAG, "getDrmKeyRequest: " +
+ " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
+ " keyType: " + keyType + " optionalParameters: " + optionalParameters);
- synchronized (mDrmLock) {
- if (!mActiveDrmScheme) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE)
- ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- HashMap<String, String> hmapOptionalParameters =
- (optionalParameters != null)
- ? new HashMap<String, String>(optionalParameters) :
- null;
-
- MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
- keyType, hmapOptionalParameters);
- Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
-
- return request;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "getDrmKeyRequest NotProvisionedException: "
- + "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "getDrmKeyRequest Exception " + e);
- throw e;
- }
-
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.getDrmKeyRequest(
+ keySetId, initData, mimeType, keyType, optionalParameters);
+ }
+ return null;
}
/**
@@ -3692,46 +3586,20 @@
* @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(
@NonNull DataSourceDesc dsd,
@Nullable byte[] keySetId, @NonNull byte[] response)
throws NoDrmSchemeException, DeniedByServerException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keySetId == null)
- ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
-
- Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response
- + " --> " + keySetResult);
-
-
- return keySetResult;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: "
- + "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("provideDrmKeyResponse: "
- + "Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "provideDrmKeyResponse Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.provideDrmKeyResponse(keySetId, response);
+ }
+ return null;
}
/**
@@ -3744,29 +3612,19 @@
* @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(
@NonNull DataSourceDesc dsd,
@NonNull byte[] keySetId)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
- synchronized (mDrmLock) {
- if (!mActiveDrmScheme) {
- Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "restoreDrmKeys: Has to set a DRM scheme first.");
- }
-
- try {
- mDrmObj.restoreKeys(mDrmSessionId, keySetId);
- } catch (Exception e) {
- Log.w(TAG, "restoreKeys Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.restoreDrmKeys(keySetId);
+ }
}
/**
@@ -3782,35 +3640,19 @@
* {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*
* @throws NoDrmSchemeException if there is no active DRM session
+ * @hide
*/
- @NonNull
public String getDrmPropertyString(
@NonNull DataSourceDesc dsd,
- @NonNull @MediaDrm.StringProperty String propertyName)
+ @NonNull @MediaDrmStringProperty String propertyName)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
- String value;
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme && !mDrmConfigAllowed) {
- Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- value = mDrmObj.getPropertyString(propertyName);
- } catch (Exception e) {
- Log.w(TAG, "getDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
-
- Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value);
-
- return value;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.getDrmPropertyString(propertyName);
+ }
+ return null;
}
/**
@@ -3825,34 +3667,25 @@
* {@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(
@NonNull DataSourceDesc dsd,
- @NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value)
+ @NonNull @MediaDrmStringProperty String propertyName, @NonNull String value)
throws NoDrmSchemeException {
// TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme && !mDrmConfigAllowed) {
- Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "setDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- mDrmObj.setPropertyString(propertyName, value);
- } catch (Exception e) {
- Log.w(TAG, "setDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.setDrmPropertyString(propertyName, value);
+ }
}
/**
* Encapsulates the DRM properties of the source.
+ * @hide
*/
public static final class DrmInfo {
private Map<UUID, byte[]> mMapPssh;
@@ -3879,36 +3712,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]);
@@ -3917,7 +3751,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)));
@@ -3927,7 +3761,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;
@@ -3991,6 +3825,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) {
@@ -3998,44 +3833,8 @@
}
}
- private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
-
- // Modular DRM helpers
-
- private void prepareDrm_createDrmStep(@NonNull UUID uuid)
- throws UnsupportedSchemeException {
- Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
-
- try {
- mDrmObj = new MediaDrm(uuid);
- Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
- } catch (Exception e) { // UnsupportedSchemeException
- Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
- throw e;
- }
- }
-
- private void prepareDrm_openSessionStep(@NonNull UUID uuid)
- throws NotProvisionedException, ResourceBusyException {
- Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
-
- // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
- // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
- // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
- try {
- mDrmSessionId = mDrmObj.openSession();
- Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
-
- // 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().
- native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
- Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
-
- } catch (Exception e) { //ResourceBusyException, NotProvisionedException
- Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
- throw e;
- }
- }
+ private native void native_prepareDrm(
+ long srcId, @NonNull byte[] uuid, @NonNull byte[] drmSessionId);
// Instantiated from the native side
@SuppressWarnings("unused")
@@ -4068,227 +3867,28 @@
}
}
- private class ProvisioningThread extends Thread {
- public static final int TIMEOUT_MS = 60000;
-
- private final DataSourceDesc mDSD;
- private UUID mUuid;
- private String mUrlStr;
- private Object mDrmLock;
- private MediaPlayer2 mMediaPlayer;
- private int mStatus;
- public int status() {
- return mStatus;
- }
-
- public ProvisioningThread(MediaDrm.ProvisionRequest request,
- DataSourceDesc dsd,
- UUID uuid, MediaPlayer2 mediaPlayer) {
- // lock is held by the caller
- mDSD = dsd;
- mDrmLock = mediaPlayer.mDrmLock;
- mMediaPlayer = mediaPlayer;
-
- mUrlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
- mUuid = uuid;
-
- mStatus = PREPARE_DRM_STATUS_PREPARATION_ERROR;
-
- Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + mUrlStr);
- }
-
- public void run() {
-
- byte[] response = null;
- boolean provisioningSucceeded = false;
- try {
- URL url = new URL(mUrlStr);
- final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- try {
- connection.setRequestMethod("POST");
- connection.setDoOutput(false);
- connection.setDoInput(true);
- connection.setConnectTimeout(TIMEOUT_MS);
- connection.setReadTimeout(TIMEOUT_MS);
-
- connection.connect();
- response = readInputStreamFully(connection.getInputStream());
-
- Log.v(TAG, "handleProvisioninig: Thread run: response "
- + response.length + " " + response);
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
- } finally {
- connection.disconnect();
- }
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
- }
-
- if (response != null) {
- try {
- mDrmObj.provideProvisionResponse(response);
- Log.v(TAG, "handleProvisioninig: Thread run: "
- + "provideProvisionResponse SUCCEEDED!");
-
- provisioningSucceeded = true;
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: "
- + "provideProvisionResponse " + e);
- }
- }
-
- boolean succeeded = false;
-
- synchronized (mDrmLock) {
- // continuing with prepareDrm
- if (provisioningSucceeded) {
- succeeded = mMediaPlayer.resumePrepareDrm(mUuid);
- mStatus = (succeeded)
- ? PREPARE_DRM_STATUS_SUCCESS :
- PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
- mMediaPlayer.mDrmProvisioningInProgress = false;
- mMediaPlayer.mPrepareDrmInProgress = false;
- if (!succeeded) {
- cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
- }
- } // synchronized
-
- // calling the callback outside the lock
- sendDrmEvent(new DrmEventNotifier() {
- @Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(
- mMediaPlayer, mDSD, mStatus);
- }
- });
-
- synchronized (mTaskLock) {
- if (mCurrentTask != null
- && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
- && mCurrentTask.mNeedToWaitForEventToComplete) {
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in', closing it when done.
- */
- private byte[] readInputStreamFully(InputStream in) throws IOException {
- try {
- return readInputStreamFullyNoClose(in);
- } finally {
- in.close();
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in'.
- */
- private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int count;
- while ((count = in.read(buffer)) != -1) {
- bytes.write(buffer, 0, count);
- }
- return bytes.toByteArray();
- }
- } // ProvisioningThread
-
- private int handleProvisioninig(DataSourceDesc dsd, UUID uuid) {
- synchronized (mDrmLock) {
- if (mDrmProvisioningInProgress) {
- Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
- if (provReq == null) {
- Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- Log.v(TAG, "handleProvisioninig provReq "
- + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
- // networking in a background thread
- mDrmProvisioningInProgress = true;
-
- mDrmProvisioningThread = new ProvisioningThread(provReq, dsd, uuid, this);
- mDrmProvisioningThread.start();
-
- return PREPARE_DRM_STATUS_SUCCESS;
- }
- }
-
- private boolean resumePrepareDrm(UUID uuid) {
- Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
-
- // mDrmLock is guaranteed to be held
- boolean success = false;
+ /**
+ * Returns a byte[] containing the remainder of 'in', closing it when done.
+ */
+ private static byte[] readInputStreamFully(InputStream in) throws IOException {
try {
- // resuming
- prepareDrm_openSessionStep(uuid);
-
- mDrmUUID = uuid;
- mActiveDrmScheme = true;
-
- success = true;
- } catch (Exception e) {
- Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed with " + e);
- // mDrmObj clean up is done by the caller
+ return readInputStreamFullyNoClose(in);
+ } finally {
+ in.close();
}
-
- return success;
}
- private void resetDrmState() {
- synchronized (mDrmLock) {
- Log.v(TAG, "resetDrmState:"
- + " mDrmInfo=" + mDrmInfo
- + " mDrmProvisioningThread=" + mDrmProvisioningThread
- + " mPrepareDrmInProgress=" + mPrepareDrmInProgress
- + " mActiveDrmScheme=" + mActiveDrmScheme);
-
- mDrmInfoResolved = false;
- mDrmInfo = null;
-
- if (mDrmProvisioningThread != null) {
- // timeout; relying on HttpUrlConnection
- try {
- mDrmProvisioningThread.join();
- } catch (InterruptedException e) {
- Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
- }
- mDrmProvisioningThread = null;
- }
-
- mPrepareDrmInProgress = false;
- mActiveDrmScheme = false;
-
- cleanDrmObj();
- } // synchronized
- }
-
- private void cleanDrmObj() {
- // the caller holds mDrmLock
- Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
-
- if (mDrmSessionId != null) {
- mDrmObj.closeSession(mDrmSessionId);
- mDrmSessionId = null;
+ /**
+ * Returns a byte[] containing the remainder of 'in'.
+ */
+ private static byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
}
- if (mDrmObj != null) {
- mDrmObj.release();
- mDrmObj = null;
- }
+ return bytes.toByteArray();
}
private static byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
@@ -4304,8 +3904,6 @@
return uuidBytes;
}
- // Modular DRM end
-
private static class TimedTextUtil {
// These keys must be in sync with the keys in TextDescription2.h
private static final int KEY_START_TIME = 7; // int
@@ -4381,6 +3979,7 @@
}
private abstract class Task implements Runnable {
+ final long mTaskId = mTaskIdGenerator.getAndIncrement();
private final int mMediaCallType;
private final boolean mNeedToWaitForEventToComplete;
private DataSourceDesc mDSD;
@@ -4472,10 +4071,510 @@
}
};
- private final class SourceInfo {
+ // Modular DRM
+ final class DrmHandle {
+
+ static final int PROVISION_TIMEOUT_MS = 60000;
+
+ final DataSourceDesc mDSD;
+ final long mSrcId;
+
+ //--- guarded by |this| start
+ MediaDrm mDrmObj;
+ byte[] mDrmSessionId;
+ UUID mActiveDrmUUID;
+ boolean mDrmConfigAllowed;
+ boolean mDrmProvisioningInProgress;
+ boolean mPrepareDrmInProgress;
+ Future<?> mProvisionResult;
+ //--- guarded by |this| end
+
+ DrmHandle(DataSourceDesc dsd, long srcId) {
+ mDSD = dsd;
+ mSrcId = srcId;
+ }
+
+ void prepare(UUID uuid) throws UnsupportedSchemeException,
+ ResourceBusyException, NotProvisionedException {
+ final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
+ Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+
+ synchronized (this) {
+ if (mActiveDrmUUID != null) {
+ final String msg = "prepareDrm(): Wrong usage: There is already "
+ + "an active DRM scheme with " + uuid;
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ if (mPrepareDrmInProgress) {
+ final String msg = "prepareDrm(): Wrong usage: There is already "
+ + "a pending prepareDrm call.";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ if (mDrmProvisioningInProgress) {
+ final String msg = "prepareDrm(): Unexpectd: Provisioning already in progress";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ // shouldn't need this; just for safeguard
+ cleanDrmObj();
+
+ mPrepareDrmInProgress = true;
+
+ try {
+ // only creating the DRM object to allow pre-openSession configuration
+ prepareDrm_createDrmStep(uuid);
+ } catch (Exception e) {
+ Log.w(TAG, "prepareDrm(): Exception ", e);
+ mPrepareDrmInProgress = false;
+ throw e;
+ }
+
+ mDrmConfigAllowed = true;
+ } // synchronized
+
+ // call the callback outside the lock
+ if (onDrmConfigHelper != null) {
+ onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
+ }
+
+ synchronized (this) {
+ mDrmConfigAllowed = false;
+ boolean earlyExit = false;
+
+ try {
+ prepareDrm_openSessionStep(uuid);
+
+ this.mActiveDrmUUID = uuid;
+ mPrepareDrmInProgress = false;
+ } catch (IllegalStateException e) {
+ final String msg = "prepareDrm(): Wrong usage: The player must be "
+ + "in the prepared state to call prepareDrm().";
+ Log.e(TAG, msg);
+ earlyExit = true;
+ mPrepareDrmInProgress = false;
+ throw new IllegalStateException(msg);
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "prepareDrm: NotProvisionedException", e);
+ throw e;
+ } catch (Exception e) {
+ Log.e(TAG, "prepareDrm: Exception " + e);
+ earlyExit = true;
+ mPrepareDrmInProgress = false;
+ throw e;
+ } finally {
+ if (earlyExit) { // clean up object if didn't succeed
+ cleanDrmObj();
+ }
+ } // finally
+ } // synchronized
+ }
+
+ void prepareDrm_createDrmStep(UUID uuid)
+ throws UnsupportedSchemeException {
+ Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
+
+ try {
+ mDrmObj = new MediaDrm(uuid);
+ Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
+ } catch (Exception e) { // UnsupportedSchemeException
+ Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
+ throw e;
+ }
+ }
+
+ void prepareDrm_openSessionStep(UUID uuid)
+ throws NotProvisionedException, ResourceBusyException {
+ Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
+
+ // TODO:
+ // don't need an open session for a future specialKeyReleaseDrm mode but we should do
+ // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
+ // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
+ try {
+ mDrmSessionId = mDrmObj.openSession();
+ Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
+
+ // 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().
+ 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
+ Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
+ throw e;
+ }
+
+ }
+
+ int handleProvisioninig(UUID uuid, long taskId) {
+ synchronized (this) {
+ if (mDrmProvisioningInProgress) {
+ Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+
+ MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+ if (provReq == null) {
+ Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+
+ Log.v(TAG, "handleProvisioninig provReq "
+ + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+
+ // networking in a background thread
+ mDrmProvisioningInProgress = true;
+
+ mProvisionResult = mDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
+
+ return PREPARE_DRM_STATUS_SUCCESS;
+ }
+ }
+
+ void provision(UUID uuid, long taskId) {
+
+ MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+ String urlStr = provReq.getDefaultUrl();
+ urlStr += "&signedRequest=" + new String(provReq.getData());
+ Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + urlStr);
+
+ byte[] response = null;
+ boolean provisioningSucceeded = false;
+ int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ try {
+ URL url = new URL(urlStr);
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ try {
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(false);
+ connection.setDoInput(true);
+ connection.setConnectTimeout(PROVISION_TIMEOUT_MS);
+ connection.setReadTimeout(PROVISION_TIMEOUT_MS);
+
+ connection.connect();
+ response = readInputStreamFully(connection.getInputStream());
+
+ Log.v(TAG, "handleProvisioninig: Thread run: response " +
+ response.length + " " + response);
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
+ } finally {
+ connection.disconnect();
+ }
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
+ }
+
+ if (response != null) {
+ try {
+ mDrmObj.provideProvisionResponse(response);
+ Log.v(TAG, "handleProvisioninig: Thread run: " +
+ "provideProvisionResponse SUCCEEDED!");
+
+ provisioningSucceeded = true;
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: " +
+ "provideProvisionResponse " + e);
+ }
+ }
+
+ boolean succeeded = false;
+
+ synchronized (this) {
+ // continuing with prepareDrm
+ if (provisioningSucceeded) {
+ succeeded = resumePrepare(uuid);
+ status = (succeeded) ?
+ PREPARE_DRM_STATUS_SUCCESS :
+ PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+ mDrmProvisioningInProgress = false;
+ mPrepareDrmInProgress = false;
+ if (!succeeded) {
+ cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
+ }
+ } // synchronized
+
+ // calling the callback outside the lock
+ final int finalStatus = status;
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmPrepared(
+ MediaPlayer2.this, mDSD, finalStatus);
+ }
+ });
+
+ synchronized (mTaskLock) {
+ if (mCurrentTask != null
+ && mCurrentTask.mTaskId == taskId
+ && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
+ && mCurrentTask.mNeedToWaitForEventToComplete) {
+ mCurrentTask = null;
+ processPendingTask_l();
+ }
+ }
+ }
+
+ Runnable newProvisioningTask(UUID uuid, long taskId) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ provision(uuid, taskId);
+ }
+ };
+ }
+
+ boolean resumePrepare(UUID uuid) {
+ Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
+
+ // mDrmLock is guaranteed to be held
+ boolean success = false;
+ try {
+ // resuming
+ prepareDrm_openSessionStep(uuid);
+
+ this.mActiveDrmUUID = uuid;
+
+ success = true;
+ } catch (Exception e) {
+ Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed:" + e);
+ // mDrmObj clean up is done by the caller
+ }
+
+ return success;
+ }
+
+ void cleanDrmObj() {
+ // the caller holds mDrmLock
+ Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
+
+ if (mDrmSessionId != null) {
+ mDrmObj.closeSession(mDrmSessionId);
+ mDrmSessionId = null;
+ }
+ if (mDrmObj != null) {
+ mDrmObj.close();
+ mDrmObj = null;
+ }
+ }
+
+ void release() throws NoDrmSchemeException {
+ synchronized (this) {
+ Log.v(TAG, "releaseDrm:");
+
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+ throw new NoDrmSchemeException(
+ "releaseDrm: No active DRM scheme to release.");
+ }
+
+ try {
+ // we don't have the player's state in this layer. The below call raises
+ // exception if we're in a non-stopped/prepared state.
+
+ // for cleaning native/mediaserver crypto object
+ native_releaseDrm(mSrcId);
+
+ // for cleaning client-side MediaDrm object; only called if above has succeeded
+ cleanDrmObj();
+
+ this.mActiveDrmUUID = null;
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "releaseDrm: Exception ", e);
+ throw new IllegalStateException(
+ "releaseDrm: The player is not in a valid state.");
+ } catch (Exception e) {
+ Log.e(TAG, "releaseDrm: Exception ", e);
+ }
+ } // synchronized
+ }
+
+ void cleanup() {
+ synchronized (this) {
+ Log.v(TAG, "cleanupDrm: " +
+ " mProvisioningTask=" + mProvisionResult +
+ " mPrepareDrmInProgress=" + mPrepareDrmInProgress +
+ " mActiveDrmScheme=" + mActiveDrmUUID);
+
+ if (mProvisionResult != null) {
+ // timeout; relying on HttpUrlConnection
+ try {
+ mProvisionResult.get();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
+ }
+ }
+
+ // set to false to avoid duplicate release calls
+ this.mActiveDrmUUID = null;
+
+ cleanDrmObj();
+ } // synchronized
+ }
+
+ Runnable newCleanupTask() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ cleanup();
+ }
+ };
+ }
+
+ MediaDrm.KeyRequest getDrmKeyRequest(
+ byte[] keySetId, byte[] initData,
+ String mimeType, int keyType,
+ Map<String, String> optionalParameters)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmKeyRequest: Has to set a DRM scheme first.");
+ }
+
+ try {
+ byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
+
+ HashMap<String, String> hmapOptionalParameters =
+ (optionalParameters != null)
+ ? new HashMap<String, String>(optionalParameters)
+ : null;
+
+ MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(
+ scope, initData, mimeType, keyType, hmapOptionalParameters);
+ Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
+
+ return request;
+
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
+ "Unexpected. Shouldn't have reached here.");
+ throw new IllegalStateException("getDrmKeyRequest: provisioning error.");
+ } catch (Exception e) {
+ Log.w(TAG, "getDrmKeyRequest Exception " + e);
+ throw e;
+ }
+
+ }
+ }
+
+ byte[] provideDrmKeyResponse(byte[] keySetId, byte[] response)
+ throws NoDrmSchemeException, DeniedByServerException {
+ synchronized (this) {
+
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmKeyRequest: Has to set a DRM scheme first.");
+ }
+
+ try {
+ byte[] scope = (keySetId == null) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
+
+ byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
+
+ Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId
+ + " response: " + response + " --> " + keySetResult);
+
+
+ return keySetResult;
+
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
+ "Unexpected. Shouldn't have reached here.");
+ throw new IllegalStateException("provideDrmKeyResponse: " +
+ "Unexpected provisioning error.");
+ } catch (Exception e) {
+ Log.w(TAG, "provideDrmKeyResponse Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ void restoreDrmKeys(byte[] keySetId)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+ if (mActiveDrmUUID == null) {
+ Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "restoreDrmKeys: Has to set a DRM scheme first.");
+ }
+
+ try {
+ mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+ } catch (Exception e) {
+ Log.w(TAG, "restoreKeys Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ String getDrmPropertyString(String propertyName)
+ throws NoDrmSchemeException {
+ String v;
+ synchronized (this) {
+
+ if (mActiveDrmUUID == null && !mDrmConfigAllowed) {
+ Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmPropertyString: Has to prepareDrm() first.");
+ }
+
+ try {
+ v = mDrmObj.getPropertyString(propertyName);
+ } catch (Exception e) {
+ Log.w(TAG, "getDrmPropertyString Exception " + e);
+ throw e;
+ }
+ } // synchronized
+
+ Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + v);
+
+ return v;
+ }
+
+ void setDrmPropertyString(String propertyName, String value)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+
+ if ( mActiveDrmUUID == null && !mDrmConfigAllowed ) {
+ Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "setDrmPropertyString: Has to prepareDrm() first.");
+ }
+
+ try {
+ mDrmObj.setPropertyString(propertyName, value);
+ } catch ( Exception e ) {
+ Log.w(TAG, "setDrmPropertyString Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ }
+
+ final class SourceInfo {
final DataSourceDesc mDSD;
final long mId = mSrcIdGenerator.getAndIncrement();
AtomicInteger mBufferedPercentage = new AtomicInteger(0);
+ boolean mClosed = false;
// m*AsNextSource (below) only applies to pending data sources in the playlist;
// the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
@@ -4483,8 +4582,25 @@
int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
boolean mPlayPendingAsNextSource = false;
+ // Modular DRM
+ final DrmHandle mDrmHandle;
+ DrmInfo mDrmInfo;
+ boolean mDrmInfoResolved;
+
SourceInfo(DataSourceDesc dsd) {
this.mDSD = dsd;
+ mDrmHandle = new DrmHandle(dsd, mId);
+ }
+
+ void close() {
+ synchronized (this) {
+ if (!mClosed) {
+ if (mDSD != null) {
+ mDSD.close();
+ }
+ mClosed = true;
+ }
+ }
}
@Override
@@ -4494,7 +4610,7 @@
}
- private SourceInfo getSourceInfoById(long srcId) {
+ private SourceInfo getSourceInfo(long srcId) {
synchronized (mSrcLock) {
if (isCurrentSource(srcId)) {
return mCurrentSourceInfo;
@@ -4506,17 +4622,68 @@
return null;
}
+ private SourceInfo getSourceInfo(DataSourceDesc dsd) {
+ synchronized (mSrcLock) {
+ if (isCurrentSource(dsd)) {
+ return mCurrentSourceInfo;
+ }
+ if (isNextSource(dsd)) {
+ return mNextSourceInfos.peek();
+ }
+ }
+ return null;
+ }
+
private boolean isCurrentSource(long srcId) {
synchronized (mSrcLock) {
return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
}
}
+ private boolean isCurrentSource(DataSourceDesc dsd) {
+ synchronized (mSrcLock) {
+ return mCurrentSourceInfo != null && mCurrentSourceInfo.mDSD == dsd;
+ }
+ }
+
private boolean isNextSource(long srcId) {
SourceInfo nextSourceInfo = mNextSourceInfos.peek();
return nextSourceInfo != null && nextSourceInfo.mId == srcId;
}
+ private boolean isNextSource(DataSourceDesc dsd) {
+ SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+ return nextSourceInfo != null && nextSourceInfo.mDSD == dsd;
+ }
+
+ @GuardedBy("mSrcLock")
+ private void setCurrentSourceInfo_l(SourceInfo sourceInfo) {
+ cleanupSourceInfo(mCurrentSourceInfo);
+ mCurrentSourceInfo = sourceInfo;
+ }
+
+ @GuardedBy("mSrcLock")
+ private void clearNextSourceInfos_l() {
+ while (!mNextSourceInfos.isEmpty()) {
+ cleanupSourceInfo(mNextSourceInfos.poll());
+ }
+ }
+
+ private void cleanupSourceInfo(SourceInfo sourceInfo) {
+ if (sourceInfo != null) {
+ sourceInfo.close();
+ Runnable task = sourceInfo.mDrmHandle.newCleanupTask();
+ mDrmThreadPool.submit(task);
+ }
+ }
+
+ private void clearSourceInfos() {
+ synchronized (mSrcLock) {
+ setCurrentSourceInfo_l(null);
+ clearNextSourceInfos_l();
+ }
+ }
+
public static final class MetricsConstants {
private MetricsConstants() {}
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/RemoteController.java b/media/java/android/media/RemoteController.java
index b6e3276..5e9eed7 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -20,7 +20,6 @@
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -36,7 +35,6 @@
import android.util.Log;
import android.view.KeyEvent;
-import java.lang.ref.WeakReference;
import java.util.List;
/**
@@ -250,7 +248,7 @@
* @throws IllegalArgumentException
*/
public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException {
- if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
throw new IllegalArgumentException("not a media key event");
}
synchronized (mInfoLock) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 436897f..874f21e 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -1029,7 +1030,7 @@
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #getDefaultUri
*/
- public static AssetFileDescriptor openDefaultRingtoneUri(
+ public static @Nullable AssetFileDescriptor openDefaultRingtoneUri(
@NonNull Context context, @NonNull Uri uri) throws FileNotFoundException {
// Try cached ringtone first since the actual provider may not be
// encryption aware, or it may be stored on CE media storage
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..bcc18ef 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(int 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 e6f39e0..4eb9e8d 100644
--- a/media/java/android/media/UriDataSourceDesc.java
+++ b/media/java/android/media/UriDataSourceDesc.java
@@ -21,8 +21,6 @@
import android.content.Context;
import android.net.Uri;
-import com.android.internal.util.Preconditions;
-
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
@@ -32,11 +30,10 @@
import java.util.Map;
/**
- * @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using URI.
*
- * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)}
- * 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}.
*
@@ -158,8 +155,8 @@
* @throws NullPointerException if context or uri is null.
*/
public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri) {
- Preconditions.checkNotNull(context, "context cannot be null");
- Preconditions.checkNotNull(uri, "uri cannot be null");
+ Media2Utils.checkArgument(context != null, "context cannot be null");
+ Media2Utils.checkArgument(uri != null, "uri cannot be null");
resetDataSource();
mUri = uri;
mContext = context;
@@ -195,8 +192,8 @@
*/
public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
- Preconditions.checkNotNull(context, "context cannot be null");
- Preconditions.checkNotNull(uri);
+ Media2Utils.checkArgument(context != null, "context cannot be null");
+ Media2Utils.checkArgument(uri != null, "uri cannot be null");
if (cookies != null) {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
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/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index 511f6cd..5411e66 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -59,6 +59,8 @@
// read next event
int count = mInputStream.read(buffer);
if (count < 0) {
+ // This is the exit condition as read() returning <0 indicates
+ // that the pipe has been closed.
break;
// FIXME - inform receivers here?
}
@@ -81,10 +83,15 @@
Log.e(TAG, "Unknown packet type " + packetType);
break;
}
- }
+ } // while (true)
} catch (IOException e) {
// FIXME report I/O failure?
- Log.e(TAG, "read failed", e);
+ // TODO: The comment above about the exit condition is not currently working
+ // as intended. The read from the closed pipe is throwing an error rather than
+ // returning <0, so this becomes (probably) not an error, but the exit case.
+ // This warrants further investigation;
+ // Silence the (probably) spurious error message.
+ // Log.e(TAG, "read failed", e);
} finally {
IoUtils.closeQuietly(mInputStream);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index c4b82c3..b457357 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -149,7 +149,7 @@
if (keyEvent == null) {
throw new IllegalArgumentException("KeyEvent may not be null");
}
- if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
return false;
}
try {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8215779..5a9a7c8 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,9 +96,15 @@
* @return The binder object from the system
* @hide
*/
+ @SystemApi
public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
- @NonNull String tag, int userId) throws RemoteException {
- return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+ @NonNull String tag, int userId) {
+ try {
+ return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null;
}
/**
@@ -620,12 +626,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/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index d9017b4..ff69779 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -59,9 +59,7 @@
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
@@ -1400,7 +1398,7 @@
// ViewRootImpl always consumes the keys. In this case, the application loses
// a chance to handle media keys. Therefore, media keys are not dispatched to
// ViewRootImpl.
- skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode())
+ skipDispatchToOverlayView = KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())
|| keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK;
} else if (event instanceof MotionEvent) {
MotionEvent motionEvent = (MotionEvent) event;
diff --git a/media/java/android/media/update/SessionToken2Provider.java b/media/java/android/media/update/SessionToken2Provider.java
deleted file mode 100644
index 95d6ce0..0000000
--- a/media/java/android/media/update/SessionToken2Provider.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface SessionToken2Provider {
- String getPackageName_impl();
- String getId_imp();
- int getType_impl();
- int getUid_impl();
- Bundle toBundle_impl();
-
- int hashCode_impl();
- boolean equals_impl(Object obj);
- String toString_impl();
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index faf4301..e25e6a5 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,7 +88,7 @@
name: "libmedia2_jni",
srcs: [
- "android_media_Media2DataSource.cpp",
+ "android_media_DataSourceCallback.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MediaPlayer2.cpp",
"android_media_SyncParams.cpp",
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index 5306de6..a3adddd 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -14,173 +14,135 @@
* limitations under the License.
*/
-#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
-#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_
#include "jni.h"
-#include <media/AudioPresentationInfo.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-
+#include <media/stagefright/foundation/ADebug.h> // CHECK
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <nativehelper/ScopedLocalRef.h>
namespace android {
struct JAudioPresentationInfo {
struct fields_t {
- jclass clazz;
+ jclass clazz = NULL;
jmethodID constructID;
// list parameters
- jclass listclazz;
+ jclass listClazz = NULL;
jmethodID listConstructId;
jmethodID listAddId;
+ // hashmap parameters
+ jclass hashMapClazz = NULL;
+ jmethodID hashMapConstructID;
+ jmethodID hashMapPutID;
+
+ // ulocale parameters
+ jclass ulocaleClazz = NULL;
+ jmethodID ulocaleConstructID;
+
void init(JNIEnv *env) {
jclass lclazz = env->FindClass("android/media/AudioPresentation");
- if (lclazz == NULL) {
- return;
- }
-
+ CHECK(lclazz != NULL);
clazz = (jclass)env->NewGlobalRef(lclazz);
- if (clazz == NULL) {
- return;
- }
-
+ CHECK(clazz != NULL);
constructID = env->GetMethodID(clazz, "<init>",
- "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
- env->DeleteLocalRef(lclazz);
+ "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
+ CHECK(constructID != NULL);
// list objects
- jclass llistclazz = env->FindClass("java/util/ArrayList");
- CHECK(llistclazz != NULL);
- listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
- CHECK(listclazz != NULL);
- listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+ jclass llistClazz = env->FindClass("java/util/ArrayList");
+ CHECK(llistClazz != NULL);
+ listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
+ CHECK(listClazz != NULL);
+ listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
CHECK(listConstructId != NULL);
- listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+ listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
CHECK(listAddId != NULL);
- env->DeleteLocalRef(llistclazz);
+
+ // hashmap objects
+ jclass lhashMapClazz = env->FindClass("java/util/HashMap");
+ CHECK(lhashMapClazz != NULL);
+ hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
+ CHECK(hashMapClazz != NULL);
+ hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
+ CHECK(hashMapConstructID != NULL);
+ hashMapPutID = env->GetMethodID(
+ hashMapClazz,
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ CHECK(hashMapPutID != NULL);
+
+ jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
+ CHECK(lulocaleClazz != NULL);
+ ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
+ CHECK(ulocaleClazz != NULL);
+ ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
+ CHECK(ulocaleConstructID != NULL);
}
void exit(JNIEnv *env) {
- env->DeleteGlobalRef(clazz);
- clazz = NULL;
- env->DeleteGlobalRef(listclazz);
- listclazz = NULL;
+ env->DeleteGlobalRef(clazz); clazz = NULL;
+ env->DeleteGlobalRef(listClazz); listClazz = NULL;
+ env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
+ env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
}
};
- static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
- ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
-
- if (hashMapClazz.get() == NULL) {
- return -EINVAL;
- }
- jmethodID hashMapConstructID =
- env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
- if (hashMapConstructID == NULL) {
- return -EINVAL;
- }
- jmethodID hashMapPutID =
- env->GetMethodID(
- hashMapClazz.get(),
- "put",
- "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
- if (hashMapPutID == NULL) {
- return -EINVAL;
- }
-
- jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
- for (size_t i = 0; i < msg->countEntries(); ++i) {
- AMessage::Type valueType;
- const char *key = msg->getEntryNameAt(i, &valueType);
-
- if (!strncmp(key, "android._", 9)) {
- // don't expose private keys (starting with android._)
- continue;
- }
- jobject valueObj = NULL;
- AString val;
- CHECK(msg->findString(key, &val));
- valueObj = env->NewStringUTF(val.c_str());
- if (valueObj != NULL) {
- ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
- if (localeClazz.get() == NULL) {
- return -EINVAL;
- }
- jmethodID localeConstructID =
- env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
- if (localeConstructID == NULL) {
- return -EINVAL;
- }
- jstring jLanguage = env->NewStringUTF(key);
- jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
- env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
- env->DeleteLocalRef(jLocale); jLocale = NULL;
- env->DeleteLocalRef(valueObj); valueObj = NULL;
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
- }
- }
-
- *map = hashMap;
-
- return OK;
+ static jobject asJobject(JNIEnv *env, const fields_t& fields) {
+ return env->NewObject(fields.listClazz, fields.listConstructId);
}
- jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
- jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
-
- for (size_t i = 0; i < info.countPresentations(); ++i) {
- const sp<AudioPresentation> &ap = info.getPresentation(i);
- jobject jLabelObject;
-
- sp<AMessage> labelMessage = new AMessage();
- for (size_t i = 0; i < ap->mLabels.size(); ++i) {
- labelMessage->setString(ap->mLabels.keyAt(i).string(),
- ap->mLabels.valueAt(i).string());
+ static void addPresentations(JNIEnv *env, const fields_t& fields,
+ const AudioPresentationCollection& presentations, jobject presentationsJObj) {
+ for (const auto& ap : presentations) {
+ ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
+ if (jLabelObject == nullptr) return;
+ ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
+ if (jLanguage == nullptr) return;
+ ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+ fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
+ ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
+ static_cast<jint>(ap.mPresentationId),
+ static_cast<jint>(ap.mProgramId),
+ jLocale.get(),
+ static_cast<jint>(ap.mMasteringIndication),
+ static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
+ static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
+ static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
+ jLabelObject.get()));
+ if (jValueObj != nullptr) {
+ env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
}
- if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
- return NULL;
- }
- ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
- if (localeClazz.get() == NULL) {
- return NULL;
- }
- jmethodID localeConstructID =
- env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
- if (localeConstructID == NULL) {
- return NULL;
- }
- jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
- jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
- jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
- static_cast<jint>(ap->mPresentationId),
- static_cast<jint>(ap->mProgramId),
- jLocale,
- static_cast<jint>(ap->mMasteringIndication),
- static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
- 1 : 0),
- static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
- 1 : 0),
- static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
- 1 : 0),
- jLabelObject);
- if (jValueObj == NULL) {
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
- return NULL;
- }
-
- env->CallBooleanMethod(list, fields.listAddId, jValueObj);
- env->DeleteLocalRef(jLocale); jLocale = NULL;
- env->DeleteLocalRef(jValueObj); jValueObj = NULL;
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
}
- return list;
+ }
+
+ private:
+ static ScopedLocalRef<jobject> convertLabelsToMap(
+ JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
+ ScopedLocalRef<jobject> nullMap(env, nullptr);
+ ScopedLocalRef<jobject> hashMap(env, env->NewObject(
+ fields.hashMapClazz, fields.hashMapConstructID));
+ if (hashMap == nullptr) {
+ return nullMap;
+ }
+
+ for (const auto& label : labels) {
+ ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
+ if (jLanguage == nullptr) return nullMap;
+ ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+ fields.ulocaleClazz,
+ fields.ulocaleConstructID,
+ jLanguage.get()));
+ if (jLocale == nullptr) return nullMap;
+ ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
+ if (jValue == nullptr) return nullMap;
+ env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
+ }
+ return hashMap;
}
};
} // namespace android
diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_DataSourceCallback.cpp
similarity index 79%
rename from media/jni/android_media_Media2DataSource.cpp
rename to media/jni/android_media_DataSourceCallback.cpp
index b3434e9..c91d409 100644
--- a/media/jni/android_media_Media2DataSource.cpp
+++ b/media/jni/android_media_DataSourceCallback.cpp
@@ -15,10 +15,10 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "JMedia2DataSource-JNI"
+#define LOG_TAG "JDataSourceCallback-JNI"
#include <utils/Log.h>
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
#include "log/log.h"
#include "jni.h"
@@ -33,14 +33,14 @@
static const size_t kBufferSize = 64 * 1024;
-JMedia2DataSource::JMedia2DataSource(JNIEnv* env, jobject source)
+JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
: mJavaObjStatus(OK),
mSizeIsCached(false),
mCachedSize(0) {
- mMedia2DataSourceObj = env->NewGlobalRef(source);
- CHECK(mMedia2DataSourceObj != NULL);
+ mDataSourceCallbackObj = env->NewGlobalRef(source);
+ CHECK(mDataSourceCallbackObj != NULL);
- ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mMedia2DataSourceObj));
+ ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
CHECK(media2DataSourceClass.get() != NULL);
mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
@@ -55,17 +55,17 @@
CHECK(mByteArrayObj != NULL);
}
-JMedia2DataSource::~JMedia2DataSource() {
+JDataSourceCallback::~JDataSourceCallback() {
JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mMedia2DataSourceObj);
+ env->DeleteGlobalRef(mDataSourceCallbackObj);
env->DeleteGlobalRef(mByteArrayObj);
}
-status_t JMedia2DataSource::initCheck() const {
+status_t JDataSourceCallback::initCheck() const {
return OK;
}
-ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) {
+ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
Mutex::Autolock lock(mLock);
if (mJavaObjStatus != OK) {
@@ -76,7 +76,7 @@
}
JNIEnv* env = JavaVMHelper::getJNIEnv();
- jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod,
+ jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
(jlong)offset, mByteArrayObj, (jint)0, (jint)size);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in readAt()");
@@ -106,7 +106,7 @@
return numread;
}
-status_t JMedia2DataSource::getSize(off64_t* size) {
+status_t JDataSourceCallback::getSize(off64_t* size) {
Mutex::Autolock lock(mLock);
if (mJavaObjStatus != OK) {
@@ -118,7 +118,7 @@
}
JNIEnv* env = JavaVMHelper::getJNIEnv();
- *size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod);
+ *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in getSize()");
jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
@@ -139,20 +139,20 @@
return OK;
}
-void JMedia2DataSource::close() {
+void JDataSourceCallback::close() {
Mutex::Autolock lock(mLock);
JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod);
+ env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
// The closed state is effectively the same as an error state.
mJavaObjStatus = UNKNOWN_ERROR;
}
-String8 JMedia2DataSource::toString() {
- return String8::format("JMedia2DataSource(pid %d, uid %d)", getpid(), getuid());
+String8 JDataSourceCallback::toString() {
+ return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
}
-String8 JMedia2DataSource::getMIMEType() const {
+String8 JDataSourceCallback::getMIMEType() const {
return String8("application/octet-stream");
}
diff --git a/media/jni/android_media_Media2DataSource.h b/media/jni/android_media_DataSourceCallback.h
similarity index 80%
rename from media/jni/android_media_Media2DataSource.h
rename to media/jni/android_media_DataSourceCallback.h
index dc085f3..5bde682 100644
--- a/media/jni/android_media_Media2DataSource.h
+++ b/media/jni/android_media_DataSourceCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
-#define _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#ifndef _ANDROID_MEDIA_DATASOURCECALLBACK_H_
+#define _ANDROID_MEDIA_DATASOURCECALLBACK_H_
#include "jni.h"
@@ -26,16 +26,16 @@
namespace android {
-// The native counterpart to a Java android.media.Media2DataSource. It inherits from
+// The native counterpart to a Java android.media.DataSourceCallback. It inherits from
// DataSource.
//
// If the java DataSource returns an error or throws an exception it
// will be considered to be in a broken state, and the only further call this
// will make is to close().
-class JMedia2DataSource : public DataSource {
+class JDataSourceCallback : public DataSource {
public:
- JMedia2DataSource(JNIEnv *env, jobject source);
- virtual ~JMedia2DataSource();
+ JDataSourceCallback(JNIEnv *env, jobject source);
+ virtual ~JDataSourceCallback();
virtual status_t initCheck() const override;
virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
@@ -56,15 +56,15 @@
bool mSizeIsCached;
off64_t mCachedSize;
- jobject mMedia2DataSourceObj;
+ jobject mDataSourceCallbackObj;
jmethodID mReadAtMethod;
jmethodID mGetSizeMethod;
jmethodID mCloseMethod;
jbyteArray mByteArrayObj;
- DISALLOW_EVIL_CONSTRUCTORS(JMedia2DataSource);
+ DISALLOW_EVIL_CONSTRUCTORS(JDataSourceCallback);
};
} // namespace android
-#endif // _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#endif // _ANDROID_MEDIA_DATASOURCECALLBACK_H_
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 15957c6..29238d3 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "MediaExtractor-JNI"
#include <utils/Log.h>
+#include "android_media_AudioPresentation.h"
#include "android_media_MediaDataSource.h"
#include "android_media_MediaExtractor.h"
#include "android_media_MediaMetricsJNI.h"
@@ -56,6 +57,7 @@
};
static fields_t gFields;
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
: mClass(NULL),
@@ -289,6 +291,10 @@
return mImpl->getCachedDuration(durationUs, eos);
}
+status_t JMediaExtractor::getAudioPresentations(size_t trackIdx,
+ AudioPresentationCollection *presentations) const {
+ return mImpl->getAudioPresentations(trackIdx, presentations);
+}
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -668,6 +674,28 @@
return JNI_TRUE;
}
+static jobject android_media_MediaExtractor_getAudioPresentations(
+ JNIEnv *env, jobject thiz, jint trackIdx) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+ jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return presentationsJObj;
+ }
+ AudioPresentationCollection presentations;
+ status_t err = extractor->getAudioPresentations(trackIdx, &presentations);
+ if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) {
+ return presentationsJObj;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return presentationsJObj;
+ }
+
+ JAudioPresentationInfo::addPresentations(
+ env, gAudioPresentationFields, presentations, presentationsJObj);
+ return presentationsJObj;
+}
+
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/MediaExtractor");
CHECK(clazz != NULL);
@@ -683,6 +711,8 @@
gFields.cryptoInfoSetPatternID =
env->GetMethodID(clazz, "setPattern", "(II)V");
+
+ gAudioPresentationFields.init(env);
}
static void android_media_MediaExtractor_native_setup(
@@ -963,6 +993,9 @@
{"native_getMetrics", "()Landroid/os/PersistableBundle;",
(void *)android_media_MediaExtractor_native_getMetrics},
+
+ { "native_getAudioPresentations", "(I)Ljava/util/List;",
+ (void *)android_media_MediaExtractor_getAudioPresentations },
};
int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index aaa8421..baa779c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -18,6 +18,7 @@
#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <media/MediaSource.h>
#include <media/DataSource.h>
#include <utils/Errors.h>
@@ -66,6 +67,8 @@
status_t getMetrics(Parcel *reply) const;
bool getCachedDuration(int64_t *durationUs, bool *eos) const;
+ status_t getAudioPresentations(size_t trackIdx,
+ AudioPresentationCollection *presentations) const;
protected:
virtual ~JMediaExtractor();
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index f7de2e7..8b6009e 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -46,7 +46,7 @@
#include "utils/KeyedVector.h"
#include "utils/String8.h"
#include "android_media_BufferingParams.h"
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
#include "android_media_SyncParams.h"
@@ -423,7 +423,7 @@
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
- sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
+ sp<DataSource> callbackDataSource = new JDataSourceCallback(env, dataSource);
sp<DataSourceDesc> dsd = new DataSourceDesc();
dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_CALLBACK;
@@ -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(
@@ -1390,7 +1390,7 @@
},
{
"nativeHandleDataSourceCallback",
- "(ZJLandroid/media/Media2DataSource;JJ)V",
+ "(ZJLandroid/media/DataSourceCallback;JJ)V",
(void *)android_media_MediaPlayer2_handleDataSourceCallback
},
{"nativePlayNextDataSource", "(J)V", (void *)android_media_MediaPlayer2_playNextDataSource},
@@ -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/proto/Android.bp b/media/proto/Android.bp
index 50d44c3..74fd525 100644
--- a/media/proto/Android.bp
+++ b/media/proto/Android.bp
@@ -7,6 +7,7 @@
srcs: ["mediaplayer2.proto"],
no_framework_libs: true,
jarjar_rules: "jarjar-rules.txt",
+ sdk_version: "28",
}
cc_library_static {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
new file mode 100644
index 0000000..f578e46
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.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.mediaframeworktest.unit;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.ImageReader;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Surface;
+
+import junit.framework.Assert;
+
+public class SurfaceUtilsTest extends junit.framework.TestCase {
+
+ @SmallTest
+ public void testInvalidSurfaceException() {
+ ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 1);
+ Surface surface = reader.getSurface();
+ surface.release();
+
+ try {
+ SurfaceUtils.isFlexibleConsumer(surface);
+ Assert.fail("unreachable");
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ reader.close();
+ }
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index e7e8384..96113d6 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -144,6 +144,7 @@
AHardwareBuffer_describe; # introduced=26
AHardwareBuffer_fromHardwareBuffer; # introduced=26
AHardwareBuffer_getNativeHandle; # introduced=26
+ AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock; # introduced=26
AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
AHardwareBuffer_release; # introduced=26
@@ -228,6 +229,9 @@
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
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.h b/native/webview/plat_support/draw_gl.h
index c8434b6..de13ed0 100644
--- a/native/webview/plat_support/draw_gl.h
+++ b/native/webview/plat_support/draw_gl.h
@@ -43,9 +43,9 @@
// Input: tells the draw function what action to perform.
enum Mode {
kModeDraw = 0,
- kModeProcess,
- kModeProcessNoContext,
- kModeSync,
+ kModeProcess = 1,
+ kModeProcessNoContext = 2,
+ kModeSync = 3,
} mode;
// Input: current clip rect in surface coordinates. Reflects the current state
@@ -93,9 +93,9 @@
AwDrawGLInfo* draw_info,
void* spare);
enum AwMapMode {
- MAP_READ_ONLY,
- MAP_WRITE_ONLY,
- MAP_READ_WRITE,
+ MAP_READ_ONLY = 0,
+ MAP_WRITE_ONLY = 1,
+ MAP_READ_WRITE = 2,
};
// Called to create a GraphicBuffer
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index 7cb49da..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] = "";
@@ -42,10 +40,10 @@
class DrawGLFunctor : public Functor {
public:
explicit DrawGLFunctor(jlong view_context) : view_context_(view_context) {}
- virtual ~DrawGLFunctor() {}
+ ~DrawGLFunctor() override {}
// Functor
- virtual status_t operator ()(int what, void* data) {
+ status_t operator ()(int what, void* data) override {
using uirenderer::DrawGlInfo;
if (!g_aw_drawgl_function) {
ALOGE("Cannot draw: no DrawGL Function installed");
@@ -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/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 8f13497..f244f9f 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -45,6 +45,7 @@
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
+ "car-theme-lib-bp",
"SystemUI-tags",
"SystemUI-proto",
],
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/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
index c98740e..709797d 100644
--- a/packages/CarSystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -20,11 +20,9 @@
android:id="@+id/volume_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@android:color/black"
android:minWidth="@dimen/volume_dialog_panel_width"
- android:theme="@style/Theme.Car.DialogListView"
- app:dividerEndMargin="@dimen/car_keyline_1"
- app:dividerStartMargin="@dimen/car_keyline_1"
+ android:theme="@style/PagedListViewTheme"
app:gutter="none"
app:scrollBarEnabled="false"
+ app:listDividerColor="@color/list_divider_color"
app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index df8f8db..c510ab6 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -51,4 +51,6 @@
<color name="car_grey_900">#ff212121</color>
<color name="keyguard_button_text_color">@android:color/black</color>
+
+ <color name="list_divider_color">@*android:color/car_list_divider_light</color>
</resources>
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/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7f4544a..0d95d30 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -41,15 +41,16 @@
<item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
</style>
- <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
- <item name="android:colorControlActivated">@color/car_accent</item>
- <item name="listItemBackgroundColor">@android:color/black</item>
- </style>
-
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
<item name="android:background">@drawable/nav_button_background</item>
</style>
-</resources>
+ <style name="PagedListViewTheme" parent="@style/Theme.CarSupportWrapper.NoActionBar">
+ <item name="android:background">@*android:color/car_background</item>
+ <item name="listItemBackgroundColor">@*android:color/car_background</item>
+ <item name="dividerEndMargin">@dimen/car_keyline_1</item>
+ <item name="dividerStartMargin">@dimen/car_keyline_1</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/xml/car_volume_items.xml b/packages/CarSystemUI/res/xml/car_volume_items.xml
index 8715946..922b1a7 100644
--- a/packages/CarSystemUI/res/xml/car_volume_items.xml
+++ b/packages/CarSystemUI/res/xml/car_volume_items.xml
@@ -23,8 +23,8 @@
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="assistance_navigation_guidance"
+ car:icon="@drawable/car_ic_navigation"/>
<item car:usage="voice_communication_signalling"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="alarm"
@@ -43,8 +43,8 @@
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="voice_communication"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="assistance_sonification"
car:icon="@drawable/car_ic_notification"/>
<item car:usage="game"
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/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
new file mode 100644
index 0000000..5bf30ca
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+
+/**
+ * AssitantButton is a ui component that will trigger the Voice Interaction Service.
+ */
+public class AssitantButton extends CarFacetButton {
+
+ private static final String TAG = "CarFacetButton";
+ private IVoiceInteractionSessionShowCallback mShowCallback =
+ new IVoiceInteractionSessionShowCallback.Stub() {
+ @Override
+ public void onFailed() {
+ Log.w(TAG, "Failed to show VoiceInteractionSession");
+ }
+
+ @Override
+ public void onShown() {
+ Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
+ }
+ };
+
+ private static final String EXTRA_CAR_PUSH_TO_TALK =
+ "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
+ private final AssistUtils mAssistUtils;
+
+ public AssitantButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mAssistUtils = new AssistUtils(context);
+ setOnClickListener(v -> {
+ showAssistant();
+ });
+ }
+
+ private void showAssistant() {
+ final Bundle args = new Bundle();
+ args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
+ mAssistUtils.showSessionForActiveService(args,
+ SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
+ }
+
+ @Override
+ protected void setupIntents(TypedArray typedArray){
+ // left blank because for the assistant button Intent will not be passed from the layout.
+ }
+}
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/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index bd32856..cea4ab0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.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;
@@ -31,6 +47,7 @@
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 static final String TAG = "CarFacetButton";
private Context mContext;
private AlphaOptimizedImageButton mIcon;
@@ -51,12 +68,10 @@
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);
@@ -64,13 +79,12 @@
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) {
+ protected 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);
@@ -111,7 +125,6 @@
}
}
-
private void setupIcons(TypedArray styledAttributes) {
mSelectedAlpha = styledAttributes.getFloat(
R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
@@ -153,6 +166,9 @@
return mFacetPackages;
}
+ /**
+ * @return The list of component names.
+ */
public String[] getComponentName() {
if (mComponentNames == null) {
return new String[0];
@@ -162,6 +178,7 @@
/**
* 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) {
@@ -171,7 +188,8 @@
/**
* 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 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
*/
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..d087176
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.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 ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
+ @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.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();
+ }
+ };
+
+ 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() {
+ try {
+ mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ 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/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 60153fc..0cad5af 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;
@@ -241,7 +240,7 @@
signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
}
if (Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) {
+ Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
if (mNotificationCategorizer.shouldSilence(entry)) {
final int importance = entry.getImportance() < IMPORTANCE_LOW
? entry.getImportance() : IMPORTANCE_LOW;
@@ -355,6 +354,37 @@
}
@Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ if (DEBUG) {
+ Log.i(TAG,
+ "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
+ + ", isExpanded = isExpanded");
+ }
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ if (DEBUG) {
+ Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
+ + "], source = [" + source + "]");
+ }
+ }
+
+ @Override
+ public void onActionClicked(String key, Notification.Action action, int source) {
+ if (DEBUG) {
+ Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+ + "], source = [" + source + "]");
+ }
+ }
+
+ @Override
public void onListenerConnected() {
if (DEBUG) Log.i(TAG, "CONNECTED");
try {
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index b2fc417..892267b 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -48,7 +48,7 @@
| 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;
private static final ConversationActions.TypeConfig TYPE_CONFIG =
@@ -81,12 +81,9 @@
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;
return suggestActionsFromText(
tcm,
- getMostSalientActionText(entry.getNotification()), maxSmartActions);
+ getMostSalientActionText(entry.getNotification()), MAX_SMART_ACTIONS);
}
ArrayList<CharSequence> suggestReplies(
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 56feb47..87d6e4a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -16,27 +16,24 @@
package com.android.location.fused;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.ProviderPropertiesUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
-import android.location.LocationProvider;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.ProviderPropertiesUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
private static final String TAG = "FusedLocationProvider";
@@ -48,7 +45,6 @@
private static final int MSG_DISABLE = 2;
private static final int MSG_SET_REQUEST = 3;
- private final Context mContext;
private final FusionEngine mEngine;
private static class RequestWrapper {
@@ -62,13 +58,12 @@
public FusedLocationProvider(Context context) {
super(TAG, PROPERTIES);
- mContext = context;
mEngine = new FusionEngine(context, Looper.myLooper());
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ context.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -122,14 +117,4 @@
// perform synchronously
mEngine.dump(fd, pw, args);
}
-
- @Override
- public int onGetStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- @Override
- public long onGetStatusUpdateTime() {
- return 0;
- }
}
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/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
deleted file mode 100644
index bc06cab..0000000
--- a/packages/PackageInstaller/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-android_app {
- name: "PackageInstaller",
-
- srcs: ["src/**/*.java"],
-
- static_libs: [
- "androidx.leanback_leanback",
- "xz-java",
- ],
-
- certificate: "platform",
- privileged: true,
- platform_apis: true,
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/packages/PackageInstaller/Android.mk
similarity index 67%
rename from services/tests/servicestests/test-apps/ConnTestApp/Android.mk
rename to packages/PackageInstaller/Android.mk
index 18b8c2d..ab5483c 100644
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
+++ b/packages/PackageInstaller/Android.mk
@@ -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.
@@ -16,17 +16,17 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_PACKAGE_NAME := PackageInstaller
-LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := ConnTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRIVATE_PLATFORM_APIS := true
-include $(BUILD_PACKAGE)
\ No newline at end of file
+LOCAL_STATIC_JAVA_LIBRARIES := xz-java
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.leanback_leanback
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 4801f62..eb9ec82 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.packageinstaller">
+ <original-package android:name="com.android.packageinstaller" />
+
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
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/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 91e23dd..03eafc4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -34,15 +34,23 @@
<uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.READ_PRINT_SERVICES" />
<uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:dataSentOffDevice="no"
+ android:dataSharedWithThirdParty="no"
+ android:dataUsedForMonetization="no"
+ android:dataRetentionTime="unlimited"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:dataSentOffDevice="no"
+ android:dataSharedWithThirdParty="no"
+ android:dataUsedForMonetization="no"
+ android:dataRetentionTime="unlimited"/>
+
<application
android:allowClearUserData="true"
android:label="@string/app_label"
- android:allowBackup= "false"
android:supportsRtl="true">
<service
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/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
new file mode 100644
index 0000000..e518e0b
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "ActionButtonsPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/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/ActionButtonsPreference/res/layout/settings_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
new file mode 100644
index 0000000..4f47113
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
@@ -0,0 +1,52 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
new file mode 100644
index 0000000..efa508d
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?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="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:drawablePadding">4dp</item>
+ <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ <item name="android:paddingTop">20dp</item>
+ <item name="android:paddingBottom">20dp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
new file mode 100644
index 0000000..8b46cc6
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * This preference provides a four buttons layout with Settings style.
+ * It looks like below
+ *
+ * --------------------------------------------------
+ * button1 | button2 | button3 | button4 |
+ * --------------------------------------------------
+ *
+ * User can set title / icon / click listener for each button.
+ *
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible(View.GONE).
+ *
+ * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
+ * 2. User doesn't set any title or icon for button.
+ */
+public class ActionButtonsPreference extends Preference {
+
+ private static final String TAG = "ActionButtonPreference";
+ private final ButtonInfo mButton1Info = new ButtonInfo();
+ private final ButtonInfo mButton2Info = new ButtonInfo();
+ private final ButtonInfo mButton3Info = new ButtonInfo();
+ private final ButtonInfo mButton4Info = new ButtonInfo();
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ setLayoutResource(R.layout.settings_action_buttons);
+ setSelectable(false);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ holder.setDividerAllowedAbove(true);
+ holder.setDividerAllowedBelow(true);
+
+ mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
+ mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
+ mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
+ mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
+
+ mButton1Info.setUpButton();
+ mButton2Info.setUpButton();
+ mButton3Info.setUpButton();
+ mButton4Info.setUpButton();
+ }
+
+ /**
+ * Set the visibility state of button1.
+ */
+ public ActionButtonsPreference setButton1Visible(boolean isVisible) {
+ if (isVisible != mButton1Info.mIsVisible) {
+ mButton1Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button1.
+ */
+ public ActionButtonsPreference setButton1Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton1Info.mText)) {
+ mButton1Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button1.
+ */
+ public ActionButtonsPreference setButton1Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton1Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button1.
+ */
+ public ActionButtonsPreference setButton1Enabled(boolean isEnabled) {
+ if (isEnabled != mButton1Info.mIsEnabled) {
+ mButton1Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button1 is clicked.
+ */
+ public ActionButtonsPreference setButton1OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton1Info.mListener) {
+ mButton1Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button2.
+ */
+ public ActionButtonsPreference setButton2Visible(boolean isVisible) {
+ if (isVisible != mButton2Info.mIsVisible) {
+ mButton2Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button2.
+ */
+ public ActionButtonsPreference setButton2Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton2Info.mText)) {
+ mButton2Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button2.
+ */
+ public ActionButtonsPreference setButton2Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton2Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button2.
+ */
+ public ActionButtonsPreference setButton2Enabled(boolean isEnabled) {
+ if (isEnabled != mButton2Info.mIsEnabled) {
+ mButton2Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button2 is clicked.
+ */
+ public ActionButtonsPreference setButton2OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton2Info.mListener) {
+ mButton2Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button3.
+ */
+ public ActionButtonsPreference setButton3Visible(boolean isVisible) {
+ if (isVisible != mButton3Info.mIsVisible) {
+ mButton3Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button3.
+ */
+ public ActionButtonsPreference setButton3Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton3Info.mText)) {
+ mButton3Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button3.
+ */
+ public ActionButtonsPreference setButton3Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton3Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button3.
+ */
+ public ActionButtonsPreference setButton3Enabled(boolean isEnabled) {
+ if (isEnabled != mButton3Info.mIsEnabled) {
+ mButton3Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button3 is clicked.
+ */
+ public ActionButtonsPreference setButton3OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton3Info.mListener) {
+ mButton3Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button4.
+ */
+ public ActionButtonsPreference setButton4Visible(boolean isVisible) {
+ if (isVisible != mButton4Info.mIsVisible) {
+ mButton4Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button4.
+ */
+ public ActionButtonsPreference setButton4Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton4Info.mText)) {
+ mButton4Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button4.
+ */
+ public ActionButtonsPreference setButton4Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton4Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button4.
+ */
+ public ActionButtonsPreference setButton4Enabled(boolean isEnabled) {
+ if (isEnabled != mButton4Info.mIsEnabled) {
+ mButton4Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button4 is clicked.
+ */
+ public ActionButtonsPreference setButton4OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton4Info.mListener) {
+ mButton4Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ static class ButtonInfo {
+ private Button mButton;
+ private CharSequence mText;
+ private Drawable mIcon;
+ private View.OnClickListener mListener;
+ private boolean mIsEnabled = true;
+ private boolean mIsVisible = true;
+
+ void setUpButton() {
+ mButton.setText(mText);
+ mButton.setOnClickListener(mListener);
+ mButton.setEnabled(mIsEnabled);
+ mButton.setCompoundDrawablesWithIntrinsicBounds(
+ null /* left */, mIcon /* top */, null /* right */, null /* bottom */);
+
+ if (shouldBeVisible()) {
+ mButton.setVisibility(View.VISIBLE);
+ } else {
+ mButton.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible.
+ *
+ * 1. User set invisible for this button. ex: mIsVisible = false.
+ * 2. User didn't set any title or icon.
+ */
+ private boolean shouldBeVisible() {
+ return mIsVisible && (!TextUtils.isEmpty(mText) || mIcon != null);
+ }
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d1f140f..cc17b25 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,6 +16,8 @@
"SettingsLibAppPreference",
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
+ "SettingsLayoutPreference",
+ "ActionButtonsPreference",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
new file mode 100644
index 0000000..813e764
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values-as/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="search_menu" msgid="1604061903696928905">"সন্ধান সম্পৰ্কীয় ছেটিংসমূহ"</string>
+</resources>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
new file mode 100644
index 0000000..489d360
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLayoutPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/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/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
new file mode 100644
index 0000000..ee4ce49
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
new file mode 100644
index 0000000..01d9c00
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -0,0 +1,105 @@
+<?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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/entity_header"
+ style="@style/EntityHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginTop="8dp"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"/>
+
+ <TextView
+ android:id="@+id/entity_header_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"/>
+
+ <TextView
+ android:id="@+id/entity_header_second_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@android:id/button1"
+ style="?android:attr/actionOverflowButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ android:minWidth="24dp"
+ android:src="@null"
+ android:tint="?android:attr/colorAccent"/>
+
+ <ImageButton
+ android:id="@android:id/button2"
+ style="?android:attr/actionOverflowButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ android:minWidth="24dp"
+ android:src="@null"
+ android:tint="?android:attr/colorAccent"/>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
new file mode 100644
index 0000000..805744b
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
@@ -0,0 +1,42 @@
+<?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="EntityHeader">
+ <item name="android:background">?android:attr/colorPrimaryDark</item>
+ <item name="android:paddingTop">24dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ <item name="android:paddingEnd">16dp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderTitle"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">20sp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderSummary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:gravity">start</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
new file mode 100644
index 0000000..2a635b0
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -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.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference can be simply customized a view by adding layout attribute in xml.
+ * User also can decide whether or not LayoutPreference allows above divider or below divider.
+ *
+ * For instances,
+ *
+ * <com.android.settingslib.widget.LayoutPreference
+ * ...
+ * android:layout="@layout/settings_entity_header"
+ * xxxxxxx:allowDividerAbove="true"
+ * xxxxxxx:allowDividerBelow="true"
+ *
+ */
+public class LayoutPreference extends Preference {
+
+ private final View.OnClickListener mClickListener = v -> performClick(v);
+ private boolean mAllowDividerAbove;
+ private boolean mAllowDividerBelow;
+ private View mRootView;
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and the supplied
+ * attribute set.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ public LayoutPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme, the supplied
+ * attribute set, and default style attribute.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies default
+ * values for the view. Can be 0 to not look for
+ * defaults.
+ */
+ public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs, defStyleAttr);
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and a customized view id.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param resource The view id which you expected to be inflated and show in preference.
+ */
+ public LayoutPreference(Context context, int resource) {
+ this(context, LayoutInflater.from(context).inflate(resource, null, false));
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and a customized view.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param view The view which you expected show in preference.
+ */
+ public LayoutPreference(Context context, View view) {
+ super(context);
+ setView(view);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
+ mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
+ R.styleable.Preference_allowDividerAbove, false);
+ mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
+ R.styleable.Preference_allowDividerBelow, false);
+ a.recycle();
+
+ a = context.obtainStyledAttributes(
+ attrs, R.styleable.Preference, defStyleAttr, 0);
+ int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0);
+ if (layoutResource == 0) {
+ throw new IllegalArgumentException("LayoutPreference requires a layout to be defined");
+ }
+ a.recycle();
+
+ // Need to create view now so that findViewById can be called immediately.
+ final View view = LayoutInflater.from(getContext())
+ .inflate(layoutResource, null, false);
+ setView(view);
+ }
+
+ private void setView(View view) {
+ setLayoutResource(R.layout.layout_preference_frame);
+ mRootView = view;
+ setShouldDisableView(false);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ holder.itemView.setOnClickListener(mClickListener);
+
+ final boolean selectable = isSelectable();
+ holder.itemView.setFocusable(selectable);
+ holder.itemView.setClickable(selectable);
+ holder.setDividerAllowedAbove(mAllowDividerAbove);
+ holder.setDividerAllowedBelow(mAllowDividerBelow);
+
+ FrameLayout layout = (FrameLayout) holder.itemView;
+ layout.removeAllViews();
+ ViewGroup parent = (ViewGroup) mRootView.getParent();
+ if (parent != null) {
+ parent.removeView(mRootView);
+ }
+ layout.addView(mRootView);
+ }
+
+ /**
+ * Finds the view with the given ID.
+ *
+ * @param id the ID to search for
+ * @return a view with given ID if found, or {@code null} otherwise
+ */
+ public <T extends View> T findViewById(int id) {
+ return mRootView.findViewById(id);
+ }
+
+ /**
+ * LayoutPreference whether or not allows to set a below divider.
+ */
+ public void setAllowDividerBelow(boolean allowed) {
+ mAllowDividerBelow = allowed;
+ }
+
+ /**
+ * Return a value whether or not LayoutPreference allows to set a below divider.
+ */
+ public boolean isAllowDividerBelow() {
+ return mAllowDividerBelow;
+ }
+}
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
new file mode 100644
index 0000000..1a47afc
--- /dev/null
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -0,0 +1,92 @@
+<?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.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="@android:color/transparent"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/checkbox_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="56dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <include layout="@layout/preference_widget_checkbox" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="64dp"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index d34820c..1dd7838 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan nie skandeer vir netwerke nie"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gestoor"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Gedeaktiveer"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-opstelling het misluk"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sal op grond van jou gebruik waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vra elke keer"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Totdat jy dit afskakel"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Sopas"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aansluitingprogram om opgedateerde grafikadrywer in ontwikkeling te gebruik"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Foonluidspreker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b595e2b..2ce5f44 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"የለም"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ተቀምጧል"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ተሰናክሏል"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"የአይ.ፒ. ውቅረት መሰናከል"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"በአጠቃቀምዎ መሠረት እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"እስከ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ገደማ ድረስ መቆየት አለበት"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"እስከ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ከ<xliff:g id="TIME_REMAINING">%1$s</xliff:g> በላይ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"የስልክ ድምጽ ማጉያ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f8c2ba2..23f3a12 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"لا يمكن فحص الشبكات"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"بدون"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غير مفعّلة"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"تعذّرت تهيئة عنوان IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك."</string>
<string name="power_discharge_by" msgid="6453537733650125582">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"حتى <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -451,4 +453,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="media_transfer_phone_device_name" msgid="1003823744105758574">"مكبر صوت الهاتف"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 72122c2..86d1459 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটৱৰ্ক বিচাৰি স্কেন কৰিব পৰা নাই"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"নাই"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ছেভ কৰি থোৱা নেটৱৰ্কসমূহ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগাৰেশ্বন বিফল হৈছে"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পৰ্যন্ত"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>তকৈও বেছি সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ফ’নৰ স্পীকাৰ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 23008e2..17d0ca6 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Şəbəkə axtarmaq olmur"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Heç biri"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Yadda saxlanılan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiv"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Konfiqurasiya Uğursuzluğu"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"İstifadəyə əsasən təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> olana qədər"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Qalan vaxt <xliff:g id="TIME_REMAINING">%1$s</xliff:g> və daha çoxdur (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Hər dəfə soruşun"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Deaktiv edənə qədər"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"İndicə"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Qrafik drayverdən istifadə etmək üçün tətbiq seçin"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon spikeri"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3ee8589..56052d2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nije moguće skenirati mreže"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfiguracija je otkazala"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> na osnovu korišćenja"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogući aplikaciju za korišćenje upravljačkog programa grafičke katice u razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 64253bf..f2c2046 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не атрымлiваецца выканаць сканаванне для сетак"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Захавана"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Адключана"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Збой канфігурацыі IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g> пры цяперашнім узроўні выкарыстання"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Зараду (<xliff:g id="LEVEL">%2$s</xliff:g>) хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Да <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць менш чым на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць больш чым на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,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="media_transfer_phone_device_name" msgid="1003823744105758574">"Дынамік тэлефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 668aa2d..5b8d6b8 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да се сканира за мрежи"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Запазено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Деактивирани"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Неуспешно конфигуриране на IP адреса"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Следва да издържи до около <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Остава/т повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Високоговорител на телефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c6eed2c..7efad99 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"কোনো কিছুই নয়"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"সংরক্ষিত"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"অক্ষম হয়েছে"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগারেশনের ব্যর্থতা"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"বর্তমান ব্যবহার অনুযায়ী আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> এর থেকেও কম বাকি আছে"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"আর <xliff:g id="THRESHOLD">%1$s</xliff:g>-এর কম চার্জ বাকি আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"আরও <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-এর বেশি চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ফেনের স্পিকার"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 16179fb..92435f7 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ne može skenirati mreže"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Greška u konfiguraciji IP-a"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g> na osnovu vaše upotrebe"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Trebala bi trajati do otprilike <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prijavi aplikaciju za korištenje ažuriranog grafičkog drajvera u razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f906ea4..3182f16 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No es poden cercar xarxes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Cap"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Desat"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivat"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuració d\'IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en fas"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Temps restant superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivis"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicació activada per utilitzar el controlador de gràfics actualitzat en desenvolupament"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altaveu del telèfon"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f9c8669..3785433 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nelze hledat sítě"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Žádné"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Uloženo"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuto"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Selhání konfigurace protokolu IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Při vašem obvyklém využití vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pokaždé se zeptat"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dokud tuto funkci nevypnete"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Právě teď"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Přihlaste aplikaci k použití vyvíjeného aktualizovaného grafického ovladače"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefonu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6b2bd98..eeeccee 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Der kan ikke søges efter netværk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gemt"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiveret"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfejl"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> baseret på dit forbrug"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Indtil <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spørg hver gang"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Indtil du deaktiverer"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Lige nu"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Tilvælg en app, der skal bruge den opdaterede grafikdriver under udvikling"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonens højttaler"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index ad6f5f2..95f7416 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Netzwerkscan nicht möglich"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Keine"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gespeichert"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiviert"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-Konfigurationsfehler"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sollte basierend auf deiner Nutzung etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Bis <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mehr als <xliff:g id="TIME_REMAINING">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Jedes Mal fragen"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Bis zur Deaktivierung"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Gerade eben"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App aktivieren, um den aktualisierten Grafiktreiber in der Entwicklung zu verwenden"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon-Lautsprecher"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 5bfbf2c..310b6cf 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Καμία"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Αποθηκευμένο"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Απενεργοποιημένο"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου, ανάλογα με τη χρήση σας"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Έως τις <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Απομένουν περισσότερα/ες από <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Ηχείο τηλεφώνου"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 6927fda..d7ed49a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphcis driver in developement"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 675084f..ecec63c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se pueden buscar las redes."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que lo desactives"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Recién"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar app para que use el controlador de gráficos actualizado en el desarrollo"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b51b847..b8fdf30 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se puede buscar redes."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardado"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Queda menos del <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Queda más del <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que se desactive"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Justo ahora"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar aplicación para usar controlador de gráficos actualizado en desarrollo"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fb077be..1246626 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Võrke ei saa kontrollida"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Puudub"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvestatud"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Keelatud"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP seadistamise ebaõnnestumine"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Peaks teie kasutuse põhjal kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Jäänud on üle <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Küsi iga kord"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kuni välja lülitate"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Äsja"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lubage rakendus, et kasutada arenduses olevat värskendatud graafikadraiverit"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoni kõlar"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3d3b8c3..68b4840 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ezin dira sareak bilatu"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Bat ere ez"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gordeta"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desgaituta"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ezin izan da konfiguratu IP helbidea"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Erabileraren arabera, ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> arte"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen dira"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Galdetu beti"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Desaktibatu arte"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Oraintxe"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Hautatu zein aplikaziorekin erabili nahi duzun garatze-prozesuan dagoen grafikoen kontrolatzaile eguneratua"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonoaren bozgorailua"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0af118e..7882b056 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکهها امکانپذیر نیست"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"هیچکدام"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ذخیرهشده"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"پیکربندی IP انجام نشد"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"باتوجه به میزان مصرفتان، باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) شارژ داشته باشید"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"تا <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> باقی مانده"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"بیش از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"بلندگوی تلفن"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index dde10d0d..1904c4d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Verkkoja ei voi etsiä."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ei mitään"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Tallennettu"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Pois käytöstä"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-kokoonpanovirhe"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Varaus loppuu käyttösi perusteella noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> saakka"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Kysy aina"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kunnes poistat sen käytöstä"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Äsken"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lisää sovellus käyttämään päivitettyä grafiikkaohjainta kehitysvaiheessa"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Puhelimen kaiutin"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1257ea5..45b2872 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivés"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>, en fonction de votre usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionnez l\'application pour utiliser le pilote graphique mis à jour en mode de conception"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 444a52e..13361d7 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivé"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionner une application pour le développement de laquelle utiliser le pilote graphique mis à jour"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8df2697..e8cf009 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Non se poden explorar redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ningunha"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivadas"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Erro na configuración de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"En función do uso, debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Ata: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo restante inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo restante: máis de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Ata a desactivación"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Subscribirse á aplicación para utilizar o controlador de gráficos actualizado en desenvolvemento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altofalante do teléfono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 4a92f2a..918920d 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"કોઈ નહીં"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"સાચવેલા"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"અક્ષમ કર્યો"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP કન્ફિગરેશન નિષ્ફળ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> સુધી"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ફોન સ્પીકર"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6d88c35..890d036 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्क के लिए स्कैन नहीं कर सकता"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"कोई नहीं"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सेव किया गया"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फ़िगरेशन की विफलता"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"आपके इस्तेमाल के हिसाब से बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> तक"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम समय बचा है"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा चलने लायक बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"फ़ोन स्पीकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 32c9a62..1464b91 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Skeniranje mreža nije moguće"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Spremljeno"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-a nije uspjela"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> na temelju vaše upotrebe"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sad"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uključi aplikaciju za upotrebu ažuriranog upravljačkog programa u razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ed19267..c9fdd45 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nem lehet beolvasni a hálózatokat"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nincs"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Mentve"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Letiltva"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurációs hiba"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"A használat alapján nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Eddig: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Kevesebb mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Mindig kérdezzen rá"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kikapcsolásig"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Az imént"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"A frissített, fejlesztés alatt álló grafikus drivert használja a választott alkalmazás"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hangszórója"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 6d516ea..3b13ed1 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Հնարավոր չէ սկանավորել ցանցերը"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ոչ մեկը"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Պահված է"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Անջատված"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP կարգավորման ձախողում"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Հեռախոսի բարձրախոս"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a5f2317..4d1b2df 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak dapat memindai jaringan"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Tidak ada"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Nonaktif"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan Anda"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tersisa lebih dari <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Selalu tanya"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Sampai Anda menonaktifkannya"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Baru saja"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ikut sertakan aplikasi untuk menggunakan driver grafis yang diupdate dalam pengembangan"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ponsel"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 6cf09a6..600ffa4 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ekki er hægt að leita að netum"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ekkert"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Vistað"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Óvirkt"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-stillingarvilla"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> miðað við notkun þína"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til klukkan <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spyrja í hvert skipti"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Þar til þú slekkur"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Rétt í þessu"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velja að nota uppfærðan myndefnisrekil í þróun í forriti"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Símahátalari"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index a1b9b82..068aa91 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossibile cercare reti"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nessuna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvata"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disattivata"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Errore configurazione IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tempo stimato rimanente in base al tuo utilizzo: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fino alle ore <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Chiedi ogni volta"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Fino alla disattivazione"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Adesso"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Attiva l\'app per utilizzare il driver grafico aggiornato nella versione di sviluppo"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlante telefono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1a50622..fa38f77 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"לא ניתן לסרוק לאיתור רשתות"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ללא"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"נשמר"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"מושבת"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"כשל בתצורת IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> על סמך השימוש במכשיר"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"נותרו יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,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="media_transfer_phone_device_name" msgid="1003823744105758574">"רמקול של טלפון"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4c544af..9927654 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ネットワークをスキャンできません"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"なし"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"保存済み"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"無効"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP設定エラー"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> まで"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>以上(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"毎回確認"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"OFF にするまで"</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="media_transfer_phone_device_name" msgid="1003823744105758574">"スマートフォンのスピーカー"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 92f3049..7ba0153 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ქსელების სკანირება არა არის შესაძლებელი"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"არცერთი"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"დამახსოვრებულია"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"გამორთულია"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP კონფიგურაციის შეფერხება"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>, მოხმარების გათვალისწინებით"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"დარჩენილია <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ზე მეტი დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ტელეფონის დინამიკი"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index b56c6fd..d5b4441 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Желілерді шолу мүмкін емес"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ешқандай"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сақталды"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Өшірілген"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясының қатесі"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Пайдалануға байланысты шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) уақытқа жетеді"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> дейін"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> шамасынан көп уақыт қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефон динамигі"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5860473..d1d1c76 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"មិនអាចវិភាគរកបណ្ដាញ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"គ្មាន"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"បានរក្សាទុក"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"បានបិទ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ការកំណត់រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> ដោយផ្អែកលើការប្រើប្រាស់របស់អ្នក"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"រហូតដល់ម៉ោង <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"នៅសល់ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ឧបករណ៍បំពងសំឡេងទូរសព្ទ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 87e18e6..46759f5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ನೆಟ್ವರ್ಕ್ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ಯಾವುದೂ ಇಲ್ಲ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ಉಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ನಿಮ್ಮ ಬಳಕೆ ಆಧರಿಸಿ <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್ ರನ್ ಆಗಬೇಕು"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ಸಮಯದವರೆಗೆ ಫೋನ್ ರನ್ ಆಗಬೇಕು"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್ ರನ್ ಆಗಬೇಕು"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ರವರೆಗೆ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ನಿಮಿಷಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3333c0d..067175b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"네트워크를 검색할 수 없습니다."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"없음"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"저장됨"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"사용 중지됨"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 설정 실패"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"사용량을 기준으로 약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>까지"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -435,7 +437,7 @@
<string name="cancel" msgid="6859253417269739139">"취소"</string>
<string name="okay" msgid="1997666393121016642">"확인"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"켜기"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"알림 일시중지 사용 설정"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"방해 금지 모드 사용 설정"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"사용 안함"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"중요 알림만"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"휴대전화 스피커"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index cbbc47c..8e994da 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Түйүндөрдү издөө мүмкүн эмес"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Жок"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сакталды"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Өчүрүлгөн"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясы бузулду"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) кийин өчөт"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> чейин"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ашыгыраак убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефондун динамиги"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 21e4679..7bf46c0 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ບໍ່ໃຊ້"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ບັນທຶກແລ້ວ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ປິດການນຳໃຊ້"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ການຕັ້ງຄ່າ IP ລົ້ມເຫຼວ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"ຈົນກວ່າຈະຮອດ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ຍັງເຫຼືອຫຼາຍກວ່າ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ລຳໂພງໂທລະສັບ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index cbff9e7..9da3d52 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nepavyksta nuskaityti tinklų"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nėra"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Išsaugotas"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Neleidžiama"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigūracijos triktis"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Pagal tai, kaip naudojama, turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Iki <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Klausti kaskart"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kol išjungsite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Ką tik"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Pasirinkti programą, kuri bus naudojama su atnaujinta kuriama grafikos tvarkykle"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefono garsiakalbis"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d200828..2233468 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nevar skenēt tīklus"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nav"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saglabāts"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Atspējots"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigurācijas kļūme"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ņemot vērā lietojumu, darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Atlikušais laiks — mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Atlicis mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Atlicis vairāk nekā <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vaicāt katru reizi"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Līdz brīdim, kad izslēgsiet"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Tikko"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Izvēlēties izmantot atjaunināto grafikas dzini šīs lietotnes izstrādē"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Tālruņa skaļrunis"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index db16847..534d154 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да скенира за мрежи"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ниедна"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Зачувано"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Оневозможено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Конфигурирањето ИП не успеа"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> според вашето користење"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уште повеќе од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефонски звучник"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c3af968..7a335cd 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"നെറ്റ്വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ഒന്നുമില്ല"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"സംരക്ഷിച്ചു"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ് (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> വരെ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ൽ കൂടുതൽ സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ഫോൺ സ്പീക്കർ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 8627e1b..4816643 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Байхгүй"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Хадгалагдсан"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Идэвхгүйжүүлсэн"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP тохируулга амжилтгүй"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Таны хэрэглээнд тулгуурлан ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> хүртэл"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"Утасны чанга яригч"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index cd7f175..8ebb182 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"काहीही नाही"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सेव्ह केले"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फिगरेशन अयशस्वी"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तुमच्या वापरावर अवलंबून सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकेल (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी शिल्लक आहे"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> पेक्षा जास्त वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनचा स्पीकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index d0b2e12..3593882 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak boleh mengimbas untuk rangkaian"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Tiada"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Dinyahdayakan"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan anda"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tinggal kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Tanya setiap kali"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Sehingga anda matikan"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Sebentar tadi"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sertakan apl untuk menggunakan pemacu grafik yang dikemas kini dalam pembangunan"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Pembesar suara telefon"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 5f5957c..7ac3742 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမျှ မဟုတ်ပါ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ပိတ်ထားသည်"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"သင်၏ အသုံးပြုမှုအပေါ် အခြေခံ၍ <xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> အထိ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ခန့်သာ ကျန်တော့သည်"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> အောက်သာ ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ကျော် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="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 3c240f8..ae9c5f2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan ikke søke etter nettverk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Lagret"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Slått av"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurasjonsfeil"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>, basert på bruken din"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spør hver gang"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Til du slår av"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Nå nettopp"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velg app for å bruke en oppdatert grafikkdriver som er under utvikling"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonhøyttaler"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1699870..0b4510fd 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"कुनै पनि होइन"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सुरक्षित गरियो"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"असक्षम पारियो"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP विन्यास असफल"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तपाईंको प्रयोगका आधारमा लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ब्याट्री लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> सम्म"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी छ"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> भन्दा बढी समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनको स्पिकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 738df94..f227e1c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan niet zoeken naar netwerken"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Opgeslagen"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Uitgeschakeld"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-configuratie mislukt"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> op basis van je gebruik"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Altijd vragen"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Totdat je uitschakelt"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Zojuist"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Meld een app aan om het geüpdatete grafische stuurprogramma in ontwikkeling te gebruiken"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoonluidspreker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 2d39cc6..bf8493a 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ନେଟ୍ୱର୍କଗୁଡ଼ିକୁ ଖୋଜିପାରୁନାହିଁ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"କିଛି ନାହିଁ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ସେଭ୍ ହୋଇଗଲା"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ଅକ୍ଷମ ହୋଇଛି"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ଆପଣଙ୍କର ବ୍ୟବହାରକୁ ଆଧାର କରି ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ବ୍ୟାଟେରୀ ପାଖାପାଖି <xliff:g id="TIME">%1$s</xliff:g> ଚାଲିବ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ରୁ କମ୍ ସମୟ ବଳକା ଅଛି"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ରୁ କମ୍ ସମୟ ବଳକା ଅଛି (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ରୁ ଅଧିକ ସମୟ ବଳକା ଅଛି(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ DND ଅନ୍ ରହିବ"</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="media_transfer_phone_device_name" msgid="1003823744105758574">"ଫୋନ୍ ସ୍ପିକର୍"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 61f0447..f948a7e 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ਕੋਈ ਨਹੀਂ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ਰੱਖਿਅਤ ਕੀਤਾ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ਅਯੋਗ ਬਣਾਇਆ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਤੋਂ ਵੱਧ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 519f82c..c75a894 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nie można wyszukać sieci."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Brak"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Zapisana"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Wyłączona"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Błąd konfiguracji IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Na podstawie Twojego sposobu korzystania jeszcze około <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Pozostało ponad: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Zawsze pytaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dopóki nie wyłączysz"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Przed chwilą"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Wybierz aplikację, która ma używać opracowywanego sterownika grafiki"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Głośnik telefonu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f01ddfa..26e4729 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização."</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até à(s) <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Resta(m) mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Até ser desativado"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Optar pela aplicação para utilizar a placa gráfica atualizada em desenvolvimento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altifalante do telemóvel"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8a7b440..94f4842 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nu se poate scana pentru rețele"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Niciuna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvată"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Dezactivată"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Eroare de configurație IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"În baza utilizării, ar trebui să reziste până la aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"a mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"A mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"A mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Întreabă de fiecare dată"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Până când dezactivați"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Chiar acum"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicația pentru înscriere pentru a folosi driverul actualizat al plăcii grafice este în dezvoltare"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Difuzorul telefonului"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 31274d1..1c331d8 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не удалось начать поиск сетей."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Нет"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сохранено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Отключено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ошибка IP-конфигурации"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"При текущем уровне использования заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Осталось менее <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит менее чем на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит более чем на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,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="media_transfer_phone_device_name" msgid="1003823744105758574">"Встроенный динамик"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index b22e068..608ff7f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"කිසිවක් නැත"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"සුරකින ලදි"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"අබලයි"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP වින්යාස කිරීම අසාර්ථකයි"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%1$s</xliff:g> පමන වන තෙක් තිබිය යුතුය"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) පමණ වන තෙක් තිබිය යුතුය"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> පමණ වන තෙක් තිබිය යුතුය"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> දක්වා"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිව ඇත"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ට වඩා වැඩියෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"දුරකථන ස්පීකරය"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 43923b8..2f76ef9 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Siete sa nedajú vyhľadávať"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Žiadne"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Uložené"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuté"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Zlyhanie konfigurácie adresy IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g> v závislosti od využitia"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Mal by vydržať približne <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vždy sa opýtať"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dokiaľ túto funkciu nevypnete"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Teraz"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prihlásiť aplikáciu, ktorá má používať aktualizovaný ovládač grafickej karty vo vývoji"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefónu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 6d392fe..64124cb 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ni mogoče iskati omrežij"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Brez"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Shranjeno"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogočeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-ja ni uspela"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Naprava bi morala glede na način uporabe delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostanek: manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostali čas delovanja: manj kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vedno vprašaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dokler ne izklopite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Pravkar"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogočena aplikacija za uporabo posodobljenega grafičnega gonilnika pri razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvočnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e05a019..25d575e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nuk mund të skanojë për rrjete"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Asnjë"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"U ruajt"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Të çaktivizuara"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Dështim në konfigurimin e IP-së"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> bazuar në përdorimin tënd"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Deri në <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> të mbetura"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mbeten më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mbeten më shumë se <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pyet çdo herë"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Deri sa ta çaktivizosh"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Pikërisht tani"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prano aplikacionin për të përdorur drejtuesin e përditësuar të grafikës që është në zhvillim"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlanti i telefonit"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 371b909..b58b47c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Није могуће скенирати мреже"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Нема"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сачувано"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Онемогућено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурација је отказала"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> на основу коришћења"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Преостало је више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,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="media_transfer_phone_device_name" msgid="1003823744105758574">"Звучник телефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e6872bb..7198b84 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Det går inte att söka efter nätverk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sparat"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inaktiverad"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfel"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> utifrån din användning"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Till kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Fråga varje gång"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Tills du inaktiverar funktionen"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Nyss"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Välj om appen ska använda den uppdaterade grafikdrivrutinen under utveckling"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Mobilens högtalare"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 23efb91..657b54f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Haiwezi kutambaza mitandao"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Hamna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Imehifadhiwa"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Imezimwa"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Haikuweza Kusanidi IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g> kulingana na jinsi unavyoitumia"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Inapaswa kudumu kwa takribani <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zimesalia zaidi ya <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uliza kila wakati"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hadi utakapoizima"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Sasa hivi"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chagua programu itakayotumia kiendeshaji cha michoro kilichosasishwa katika hatua ya kusanidi"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Spika ya simu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 53ba738..59b42d8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"நீங்கள் பயன்படுத்துவதன் அடிப்படையில் <xliff:g id="TIME">%1$s</xliff:g> வரை உபயோகிக்க முடியும்"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும்"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> வரை"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும்"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>க்கும் மேல் பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"மொபைல் ஸ்பீக்கர்"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c8c6b4c..af233da 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"నెట్వర్క్ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ఏదీ లేదు"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"సేవ్ చేయబడింది"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"నిలిపివేయబడింది"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"మీ వినియోగం ఆధారంగా దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> వరకు"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ఫోన్ స్పీకర్"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index d293d59..636c3f2 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ไม่สามารถสแกนหาเครือข่าย"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ไม่มี"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"บันทึกแล้ว"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ปิดอยู่"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"การกำหนดค่า IP ล้มเหลว"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> เมื่อดูจากการใช้งานของคุณ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"จนถึง <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"เหลืออีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"เหลือเวลาอีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"เหลือเวลามากกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"ลำโพงโทรศัพท์"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 9da4561..1bcc36a 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Hindi makapag-scan ng mga network"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Wala"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Na-save"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Naka-disable"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Pagkabigo ng Configuration ng IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> batay sa iyong paggamit"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hanggang <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Magtanong palagi"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hanggang sa i-off mo"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Ngayon lang"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App sa pag-opt in para magamit ang na-update na graphics driver na ginagawa"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ng telepono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 2d5cd7f..ff669f6 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ağlar taranamıyor"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Yok"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Kaydedildi"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Devre dışı"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Yapılandırması Hatası"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kullanımınıza göre saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Şu saate kadar: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"En fazla <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"En çok <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"En az <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Her zaman sor"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Siz kapatana kadar"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Az önce"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Güncellenmiş grafik sürücüsünü geliştirme ortamında kullanmak için uygulamayı kaydedin"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hoparlörü"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 512ea86..2ded8c1 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Неможливо здійснити сканування мереж"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Немає"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Збережено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Вимкнено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Помилка конфігурації IP-адреси"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"На основі даних про використання, вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Залишилося понад <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,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="media_transfer_phone_device_name" msgid="1003823744105758574">"Динамік телефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 84ad3ed..7dc4690 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"کوئی نہیں"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"محفوظ کردیا گیا"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیر فعال"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP کنفیگریشن کی ناکامی"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> سے زیادہ باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"فون اسپیکر"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index af2ada5..5f92c9a 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Hech qanday"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saqlandi"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"O‘chiq"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP manzilini sozlab bo‘lmadi"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Joriy holatda taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> gacha"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan ko‘proq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Har safar so‘ralsin"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Bekor qilinmaguncha"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Hozir"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ilova yangilangan grafik drayverdan (hali ishlov jarayonida) foydalanadi"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon karnayi"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index f454127..eb10fb1 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Không thể dò tìm mạng"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Không"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Đã lưu"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Đã tắt"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Lỗi cấu hình IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Cho đến <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Còn lại hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Luôn hỏi"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Cho đến khi bạn tắt"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Vừa xong"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chọn ứng dụng để sử dụng trình điều khiển đồ họa được cập nhật trong giai đoạn phát triển"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Loa điện thoại"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 10a20be..7b14138 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"无法扫描网络"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"无"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已保存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 配置失败"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根据您的使用情况,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"目前电量为 <xliff:g id="LEVEL">%2$s</xliff:g>,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"直到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"电量剩余使用时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"电量剩余使用时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"手机扬声器"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 1510545..48b6959 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃瞄網絡"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據您的使用情況,電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還可用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩餘電量時間少於 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"還有少於 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"還有超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 90c0d80..684569b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃描網路"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據你的使用情形,預估可持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"目前電量 <xliff:g id="LEVEL">%2$s</xliff:g>,預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +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="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e4f9f4d..d107a5a 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ayikwazi ukuhlola amanethiwekhi"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Lutho"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Kulondoloziwe"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Akusebenzi"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ukwehluleka kokulungiswa kwe-IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> kusukela ekusetshenzisweni kwakho"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuze kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kusele okungaphansi kunokungu-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Ngaphansi kuka-<xliff:g id="THRESHOLD">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Ngaphezu kuka-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Buza njalo"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Uze uvale isikrini"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Khona manje"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uhlelo lokusebenza lokukhetha ukungena olungasebenzisa idrayivu yamagrafikhi ekuthuthukiseni"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Isipikha sefoni"</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 45a3bb0..9270d13 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -54,18 +54,17 @@
private final Context mContext;
private final BluetoothAdapter mLocalAdapter;
private final LocalBluetoothProfileManager mProfileManager;
+ private final Object mProfileLock = new Object();
BluetoothDevice mDevice;
private long mHiSyncId;
// Need this since there is no method for getting RSSI
short mRssi;
// mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
// because current sub device is only for HearingAid and its profile is the same.
- private final List<LocalBluetoothProfile> mProfiles =
- Collections.synchronizedList(new ArrayList<>());
+ private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>();
// List of profiles that were previously in mProfiles, but have been removed
- private final List<LocalBluetoothProfile> mRemovedProfiles =
- Collections.synchronizedList(new ArrayList<>());
+ private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>();
// Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
private boolean mLocalNapRoleConnected;
@@ -74,7 +73,7 @@
private int mMessageRejectionCount;
- private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
+ 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;
@@ -134,36 +133,42 @@
}
return;
}
- if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
- if (profile instanceof MapProfile) {
- profile.setPreferred(mDevice, true);
- }
- if (!mProfiles.contains(profile)) {
- mRemovedProfiles.remove(profile);
- mProfiles.add(profile);
- if (profile instanceof PanProfile &&
- ((PanProfile) profile).isLocalRoleNap(mDevice)) {
- // Device doesn't support NAP, so remove PanProfile on disconnect
- mLocalNapRoleConnected = true;
+
+ synchronized (mProfileLock) {
+ if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
+ if (profile instanceof MapProfile) {
+ profile.setPreferred(mDevice, true);
}
+ if (!mProfiles.contains(profile)) {
+ mRemovedProfiles.remove(profile);
+ mProfiles.add(profile);
+ if (profile instanceof PanProfile
+ && ((PanProfile) profile).isLocalRoleNap(mDevice)) {
+ // Device doesn't support NAP, so remove PanProfile on disconnect
+ mLocalNapRoleConnected = true;
+ }
+ }
+ } else if (profile instanceof MapProfile
+ && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ profile.setPreferred(mDevice, false);
+ } else if (mLocalNapRoleConnected && profile instanceof PanProfile
+ && ((PanProfile) profile).isLocalRoleNap(mDevice)
+ && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
+ mProfiles.remove(profile);
+ mRemovedProfiles.add(profile);
+ mLocalNapRoleConnected = false;
}
- } else if (profile instanceof MapProfile &&
- newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
- profile.setPreferred(mDevice, false);
- } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
- ((PanProfile) profile).isLocalRoleNap(mDevice) &&
- newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
- Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
- mProfiles.remove(profile);
- mRemovedProfiles.add(profile);
- mLocalNapRoleConnected = false;
}
+
fetchActiveDevices();
}
public void disconnect() {
- for (LocalBluetoothProfile profile : mProfiles) {
- disconnect(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ disconnect(profile);
+ }
}
// Disconnect PBAP server in case its connected
// This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -203,6 +208,10 @@
mHiSyncId = id;
}
+ public boolean isHearingAidDevice() {
+ return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
@@ -210,32 +219,35 @@
}
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
- // Try to initialize the profiles if they were not.
- if (mProfiles.isEmpty()) {
- // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
- // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
- // from bluetooth stack but ACTION.uuid is not sent yet.
- // Eventually ACTION.uuid will be received which shall trigger the connection of the
- // various profiles
- // If UUIDs are not available yet, connect will be happen
- // upon arrival of the ACTION_UUID intent.
- Log.d(TAG, "No profiles. Maybe we will connect later");
- return;
- }
+ synchronized (mProfileLock) {
+ // Try to initialize the profiles if they were not.
+ if (mProfiles.isEmpty()) {
+ // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
+ // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been
+ // updated from bluetooth stack but ACTION.uuid is not sent yet.
+ // Eventually ACTION.uuid will be received which shall trigger the connection of the
+ // various profiles
+ // If UUIDs are not available yet, connect will be happen
+ // upon arrival of the ACTION_UUID intent.
+ Log.d(TAG, "No profiles. Maybe we will connect later");
+ return;
+ }
- int preferredProfiles = 0;
- for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
- if (profile.isPreferred(mDevice)) {
- ++preferredProfiles;
- connectInt(profile);
+ int preferredProfiles = 0;
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (connectAllProfiles ? profile.accessProfileEnabled()
+ : profile.isAutoConnectable()) {
+ if (profile.isPreferred(mDevice)) {
+ ++preferredProfiles;
+ connectInt(profile);
+ }
}
}
- }
- if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
+ if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
- if (preferredProfiles == 0) {
- connectAutoConnectableProfiles();
+ if (preferredProfiles == 0) {
+ connectAutoConnectableProfiles();
+ }
}
}
@@ -244,10 +256,12 @@
return;
}
- for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.isAutoConnectable()) {
- profile.setPreferred(mDevice, true);
- connectInt(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (profile.isAutoConnectable()) {
+ profile.setPreferred(mDevice, true);
+ connectInt(profile);
+ }
}
}
}
@@ -515,14 +529,16 @@
* @return Whether it is connected.
*/
public boolean isConnected() {
- for (LocalBluetoothProfile profile : mProfiles) {
- int status = getProfileConnectionState(profile);
- if (status == BluetoothProfile.STATE_CONNECTED) {
- return true;
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTED) {
+ return true;
+ }
}
- }
- return false;
+ return false;
+ }
}
public boolean isConnectedProfile(LocalBluetoothProfile profile) {
@@ -532,14 +548,16 @@
}
public boolean isBusy() {
- for (LocalBluetoothProfile profile : mProfiles) {
- int status = getProfileConnectionState(profile);
- if (status == BluetoothProfile.STATE_CONNECTING
- || status == BluetoothProfile.STATE_DISCONNECTING) {
- return true;
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTING
+ || status == BluetoothProfile.STATE_DISCONNECTING) {
+ return true;
+ }
}
+ return getBondState() == BluetoothDevice.BOND_BONDING;
}
- return getBondState() == BluetoothDevice.BOND_BONDING;
}
private boolean updateProfiles() {
@@ -554,8 +572,10 @@
*/
processPhonebookAccess();
- mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
- mLocalNapRoleConnected, mDevice);
+ synchronized (mProfileLock) {
+ mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
+ mLocalNapRoleConnected, mDevice);
+ }
if (BluetoothUtils.D) {
Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
@@ -616,7 +636,9 @@
void onBondingStateChanged(int bondState) {
if (bondState == BluetoothDevice.BOND_NONE) {
- mProfiles.clear();
+ synchronized (mProfileLock) {
+ mProfiles.clear();
+ }
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
@@ -646,9 +668,11 @@
public List<LocalBluetoothProfile> getConnectableProfiles() {
List<LocalBluetoothProfile> connectableProfiles =
new ArrayList<LocalBluetoothProfile>();
- for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.accessProfileEnabled()) {
- connectableProfiles.add(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (profile.accessProfileEnabled()) {
+ connectableProfiles.add(profile);
+ }
}
}
return connectableProfiles;
@@ -823,10 +847,12 @@
public int getMaxConnectionState() {
int maxState = BluetoothProfile.STATE_DISCONNECTED;
- for (LocalBluetoothProfile profile : getProfiles()) {
- int connectionStatus = getProfileConnectionState(profile);
- if (connectionStatus > maxState) {
- maxState = connectionStatus;
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
+ if (connectionStatus > maxState) {
+ maxState = connectionStatus;
+ }
}
}
return maxState;
@@ -843,32 +869,34 @@
boolean hfpConnected = true; // HFP is connected
boolean hearingAidConnected = true; // Hearing Aid is connected
- for (LocalBluetoothProfile profile : getProfiles()) {
- int connectionStatus = getProfileConnectionState(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
- switch (connectionStatus) {
- case BluetoothProfile.STATE_CONNECTING:
- case BluetoothProfile.STATE_DISCONNECTING:
- return mContext.getString(
- BluetoothUtils.getConnectionStateSummary(connectionStatus));
+ switch (connectionStatus) {
+ case BluetoothProfile.STATE_CONNECTING:
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return mContext.getString(
+ BluetoothUtils.getConnectionStateSummary(connectionStatus));
- case BluetoothProfile.STATE_CONNECTED:
- profileConnected = true;
- break;
+ case BluetoothProfile.STATE_CONNECTED:
+ profileConnected = true;
+ break;
- case BluetoothProfile.STATE_DISCONNECTED:
- if (profile.isProfileReady()) {
- if ((profile instanceof A2dpProfile) ||
- (profile instanceof A2dpSinkProfile)) {
- a2dpConnected = false;
- } else if ((profile instanceof HeadsetProfile) ||
- (profile instanceof HfpClientProfile)) {
- hfpConnected = false;
- } else if (profile instanceof HearingAidProfile) {
- hearingAidConnected = false;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (profile.isProfileReady()) {
+ if (profile instanceof A2dpProfile
+ || profile instanceof A2dpSinkProfile) {
+ a2dpConnected = false;
+ } else if (profile instanceof HeadsetProfile
+ || profile instanceof HfpClientProfile) {
+ hfpConnected = false;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidConnected = false;
+ }
}
- }
- break;
+ break;
+ }
}
}
@@ -924,32 +952,34 @@
boolean hfpNotConnected = false; // HFP is preferred but not connected
boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
- for (LocalBluetoothProfile profile : getProfiles()) {
- int connectionStatus = getProfileConnectionState(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
- switch (connectionStatus) {
- case BluetoothProfile.STATE_CONNECTING:
- case BluetoothProfile.STATE_DISCONNECTING:
- return mContext.getString(
- BluetoothUtils.getConnectionStateSummary(connectionStatus));
+ switch (connectionStatus) {
+ case BluetoothProfile.STATE_CONNECTING:
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return mContext.getString(
+ BluetoothUtils.getConnectionStateSummary(connectionStatus));
- case BluetoothProfile.STATE_CONNECTED:
- profileConnected = true;
- break;
+ case BluetoothProfile.STATE_CONNECTED:
+ profileConnected = true;
+ break;
- case BluetoothProfile.STATE_DISCONNECTED:
- if (profile.isProfileReady()) {
- if ((profile instanceof A2dpProfile) ||
- (profile instanceof A2dpSinkProfile)){
- a2dpNotConnected = true;
- } else if ((profile instanceof HeadsetProfile) ||
- (profile instanceof HfpClientProfile)) {
- hfpNotConnected = true;
- } else if (profile instanceof HearingAidProfile) {
- hearingAidNotConnected = true;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (profile.isProfileReady()) {
+ if (profile instanceof A2dpProfile
+ || profile instanceof A2dpSinkProfile) {
+ a2dpNotConnected = true;
+ } else if (profile instanceof HeadsetProfile
+ || profile instanceof HfpClientProfile) {
+ hfpNotConnected = true;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidNotConnected = true;
+ }
}
- }
- break;
+ break;
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index f7f6589..3a62838 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -50,7 +50,7 @@
}
public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
- return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
+ return new ArrayList<>(mCachedDevices);
}
public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 92fd868..12b8efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -53,6 +53,8 @@
"com.android.settings.category.ia.night_display";
public static final String CATEGORY_PRIVACY =
"com.android.settings.category.ia.privacy";
+ public static final String CATEGORY_ENTERPRISE_PRIVACY =
+ "com.android.settings.category.ia.enterprise_privacy";
public static final Map<String, String> KEY_COMPAT_MAP;
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/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 86928fc..4f81daf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -154,8 +154,10 @@
static final String KEY_CARRIER_NAME = "key_carrier_name";
static final AtomicInteger sLastId = new AtomicInteger(0);
- /**
- * These values are matched in string arrays -- changes must be kept in sync
+ /*
+ * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState,
+ * and sent across IPC. The numeric values should remain stable, otherwise the changes will need
+ * to be synced with other unbundled users of this library.
*/
public static final int SECURITY_NONE = 0;
public static final int SECURITY_WEP = 1;
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index d15a3ef..cfa067f 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -32,12 +32,13 @@
include $(BUILD_PACKAGE)
-#############################################
-# SettingsLib Robolectric test target. #
-#############################################
+############################################################
+# SettingsLib Robolectric test target. #
+############################################################
include $(CLEAR_VARS)
LOCAL_MODULE := SettingsLibRoboTests
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -53,6 +54,9 @@
LOCAL_MODULE_TAGS := optional
+# Generate test_config.properties
+include external/robolectric-shadows/gen_test_config.mk
+
include $(BUILD_STATIC_JAVA_LIBRARY)
#############################################################
diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties
index 6b5b8e5..fab7251 100644
--- a/packages/SettingsLib/tests/robotests/config/robolectric.properties
+++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties
@@ -1,5 +1 @@
-manifest=frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml
sdk=NEWEST_SDK
-
-shadows=\
- com.android.settingslib.testutils.shadow.ShadowXmlUtils
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
index 9ba9967..3a4e2e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
@@ -30,10 +30,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceComaptTest {
@Mock
@@ -70,7 +71,7 @@
}
private static class TestPreference extends CustomEditTextPreferenceCompat {
- public TestPreference(Context context) {
+ private TestPreference(Context context) {
super(context);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
index 9d7f59a..e94a06c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
@@ -30,10 +30,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceTest {
@Mock
@@ -70,7 +71,7 @@
}
private static class TestPreference extends CustomEditTextPreference {
- public TestPreference(Context context) {
+ private TestPreference(Context context) {
super(context);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
index 19a916c..4e8af73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
@@ -24,9 +24,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DeviceInfoUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index 36b70df..4d76331 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -18,12 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.R;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -36,20 +37,19 @@
import android.provider.Settings;
import android.view.MenuItem;
-import android.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Tests for {@link HelpUtils}.
*/
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class HelpUtilsTest {
private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
private static final String PACKAGE_NAME_KEY = "package-name-key";
@@ -83,8 +83,6 @@
when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey))
.thenReturn(FEEDBACK_INTENT_NAME_KEY);
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
-
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 88ac8ce..2b5a4e0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -25,8 +25,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
@@ -44,11 +44,12 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.Collections;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RestrictedLockUtilsTest {
@Mock
@@ -178,8 +179,7 @@
public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -207,8 +207,7 @@
public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -231,8 +230,7 @@
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -268,8 +266,7 @@
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any())
.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 79d682d6..1b10c73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib;
-
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -35,8 +34,9 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceHelperTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
deleted file mode 100644
index ede248b..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ /dev/null
@@ -1,76 +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.settingslib;
-
-import android.annotation.NonNull;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.manifest.AndroidManifest;
-import org.robolectric.res.Fs;
-import org.robolectric.res.ResourcePath;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner {
-
- public SettingsLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
- super(testClass);
- }
-
- /**
- * We are going to create our own custom manifest so we can add multiple resource paths to it.
- */
- @Override
- protected AndroidManifest getAppManifest(Config config) {
- try {
- // Using the manifest file's relative path, we can figure out the application directory.
- final URL appRoot =
- new URL("file:frameworks/base/packages/SettingsLib/tests/robotests");
- final URL manifestPath = new URL(appRoot, "AndroidManifest.xml");
- final URL resDir = new URL(appRoot, "res");
- final URL assetsDir = new URL(appRoot, "assets");
-
- return new AndroidManifest(Fs.fromURL(manifestPath), Fs.fromURL(resDir),
- Fs.fromURL(assetsDir), "com.android.settingslib") {
- @Override
- public List<ResourcePath> getIncludedResourcePaths() {
- final List<ResourcePath> paths = super.getIncludedResourcePaths();
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
- paths.add(resourcePath("file:frameworks/base/core/res/res"));
- paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
- return paths;
- }
- };
- } catch (MalformedURLException e) {
- throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
- }
- }
-
- private static ResourcePath resourcePath(@NonNull String spec) {
- try {
- return new ResourcePath(null, Fs.fromURL(new URL(spec)), null);
- } catch (MalformedURLException e) {
- throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
- }
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
index e70baa1..0ca7791 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
@@ -32,12 +32,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TetherUtilTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
index c0b69f2..3f0ba13 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
@@ -36,9 +36,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TwoTargetPreferenceTest {
private PreferenceViewHolder mViewHolder;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 08a75ab..594d767 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -49,6 +49,7 @@
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -58,10 +59,8 @@
import java.util.HashMap;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {
- UtilsTest.ShadowSecure.class,
- UtilsTest.ShadowLocationManager.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String PERCENTAGE_0 = "0%";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
index 152d024..44fdaec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -23,14 +23,13 @@
import android.os.UserHandle;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AccessibilityUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index b307b47..ccec175a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -41,7 +41,6 @@
import android.os.UserHandle;
import android.util.IconDrawableFactory;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
@@ -55,6 +54,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -67,7 +67,7 @@
import java.util.List;
import java.util.UUID;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class,
ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
ApplicationsStateRoboTest.ShadowPackageManager.class})
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
index a92a2dd..50fad70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -18,8 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -32,16 +32,15 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DefaultAppInfoTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
index d8c459c..f7fd25b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
@@ -26,14 +26,13 @@
import android.content.ComponentName;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ServiceListingTest {
private static final String TEST_SETTING = "testSetting";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 29831a8..c555cbe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -17,8 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -32,7 +32,6 @@
import android.content.res.Resources;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -40,26 +39,27 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpProfileTest {
@Mock
- Context mContext;
+ private Context mContext;
@Mock
- CachedBluetoothDeviceManager mDeviceManager;
+ private CachedBluetoothDeviceManager mDeviceManager;
@Mock
- LocalBluetoothProfileManager mProfileManager;
+ private LocalBluetoothProfileManager mProfileManager;
@Mock
- BluetoothDevice mDevice;
+ private BluetoothDevice mDevice;
@Mock
- BluetoothA2dp mBluetoothA2dp;
- BluetoothProfile.ServiceListener mServiceListener;
+ private BluetoothA2dp mBluetoothA2dp;
+ private BluetoothProfile.ServiceListener mServiceListener;
- A2dpProfile mProfile;
+ private A2dpProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index 274fff8..976445e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpSinkProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothA2dpSink mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpSinkProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index c147d5e..27b8dfc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -29,20 +29,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.Handler;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothEventManagerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 07310bd..0eb6de9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -21,14 +21,14 @@
import android.graphics.drawable.Drawable;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothUtilsTest {
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 9c75491..47b1210 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -28,18 +28,17 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5ceede1..4e5d38a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -31,16 +31,15 @@
import android.content.Context;
import android.media.AudioManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
private final static String DEVICE_NAME = "TestName";
private final static String DEVICE_ALIAS = "TestAlias";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index c0a1f0c..9adef82 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -11,7 +11,6 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -19,11 +18,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HeadsetProfileTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index cb1b12d..2b5466c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -29,16 +29,15 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class HearingAidDeviceManagerTest {
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 187be0b..69c020d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HfpClientProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothHeadsetClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HfpClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
index c91ee22..f38af70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HidDeviceProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothHidDevice mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HidDeviceProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index a3c3a54..5d5872e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,7 +37,6 @@
import android.content.Intent;
import android.os.ParcelUuid;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -45,6 +44,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@@ -52,7 +52,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
private final static long HISYNCID = 10;
@@ -270,13 +270,13 @@
verify(mCachedBluetoothDevice).refresh();
}
- private List<Integer> generateList(int[] profile) {
- if (profile == null) {
+ private List<Integer> generateList(int[] profiles) {
+ if (profiles == null) {
return null;
}
- final List<Integer> profileList = new ArrayList<>(profile.length);
- for(int i = 0; i < profile.length; i++) {
- profileList.add(profile[i]);
+ final List<Integer> profileList = new ArrayList<>(profiles.length);
+ for (int profile : profiles) {
+ profileList.add(profile);
}
return profileList;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index c4c48a8..6f66709 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class MapClientProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothMapClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private MapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index e4a444c..b21ec9c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,12 +33,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothAdapter.class)
public class PbapClientProfileTest {
@Mock
@@ -52,8 +49,6 @@
@Mock
private BluetoothPbapClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private PbapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index 9bb53ee..ec88034 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSap;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class SapProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothSap mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private SapProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 4d7553c..28de191 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -24,16 +24,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AbstractPreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 4ec6fb2..8a0ae91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -35,13 +34,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class MetricsFeatureProviderTest {
@Mock
private LogWriter mLogWriter;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
index 6285fcd..8f51dec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -17,8 +17,8 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -26,16 +26,15 @@
import android.content.Context;
import android.content.SharedPreferences;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SharedPreferenceLoggerTest {
private static final String TEST_TAG = "tag";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index b251c09..097db17 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -17,10 +17,10 @@
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -35,7 +35,6 @@
import androidx.fragment.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -43,10 +42,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class VisibilityLoggerMixinTest {
@Mock
@@ -139,7 +138,7 @@
private final class TestInstrumentable implements Instrumentable {
- public static final int TEST_METRIC = 12345;
+ private static final int TEST_METRIC = 12345;
@Override
public int getMetricsCategory() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 887c1d5..29e37e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -28,7 +28,6 @@
import androidx.lifecycle.LifecycleOwner;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.events.OnAttach;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
@@ -43,10 +42,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.androidx.fragment.FragmentController;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LifecycleTest {
private LifecycleOwner mLifecycleOwner;
@@ -56,7 +56,7 @@
final TestObserver mFragObserver;
- public TestDialogFragment() {
+ private TestDialogFragment() {
mFragObserver = new TestObserver();
mLifecycle.addObserver(mFragObserver);
}
@@ -236,11 +236,11 @@
}
private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
- public boolean wasCalled = false;
+ private boolean mWasCalled = false;
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
- wasCalled = true;
+ mWasCalled = true;
return false;
}
}
@@ -258,14 +258,14 @@
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
- assertThat(accepter.wasCalled).isFalse();
+ assertThat(accepter.mWasCalled).isFalse();
}
private class OnStartObserver implements LifecycleObserver, OnStart {
private final Lifecycle mLifecycle;
- public OnStartObserver(Lifecycle lifecycle) {
+ private OnStartObserver(Lifecycle lifecycle) {
mLifecycle = lifecycle;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
index 9dd93b3a..6191a00 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
@@ -22,16 +22,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DeveloperOptionsPreferenceControllerTest {
private static final String TEST_KEY = "Test_pref_key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a0fa6b5..3475ff7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -18,23 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
-import android.os.UserManager;
import android.content.Context;
+import android.os.UserManager;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadow.api.Shadow;
-
-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 org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DevelopmentSettingsEnablerTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index d7b23b0..e84a25c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -32,17 +32,16 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class EnableAdbPreferenceControllerTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@@ -150,7 +149,7 @@
}
class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
- public ConcreteEnableAdbPreferenceController(Context context) {
+ private ConcreteEnableAdbPreferenceController(Context context) {
super(context);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
index 2f78899..146be23 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -45,16 +45,16 @@
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LogdSizePreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index ed128e0..d5afb4b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -29,7 +29,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,9 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LogpersistPreferenceControllerTest {
private LifecycleOwner mLifecycleOwner;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
index 40db478..d1212fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
@@ -27,16 +27,15 @@
import android.os.IBinder;
import android.os.Parcel;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SystemPropPokerTest {
@Spy
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
index 234b4d5..16de5f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
@@ -26,7 +26,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -34,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothAddressPreferenceControllerTest {
@Mock
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index aee956c..4444e63 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -30,7 +30,6 @@
import android.content.IntentFilter;
import android.os.Handler;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -39,8 +38,9 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ConnectivityPreferenceControllerTest {
@Mock
private Context mContext;
@@ -91,8 +91,7 @@
private static class ConcreteConnectivityPreferenceController
extends AbstractConnectivityPreferenceController {
-
- public ConcreteConnectivityPreferenceController(Context context,
+ private ConcreteConnectivityPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
index 2b490ee..bd223bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
@@ -25,12 +25,10 @@
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -38,11 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowSubscriptionManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ImsStatusPreferenceControllerTest {
@Mock
private Context mContext;
@@ -61,8 +58,9 @@
}
@Test
- @Config(shadows = ShadowSubscriptionManager.class)
public void testIsAvailable() {
+ ShadowSubscriptionManager.setDefaultDataSubscriptionId(1234);
+
CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
@@ -92,18 +90,10 @@
.that(imsStatusPreferenceController.isAvailable()).isFalse();
}
- @Implements(SubscriptionManager.class)
- public static class ShadowSubscriptionManager {
- @Implementation
- public static int getDefaultDataSubscriptionId() {
- return 1234;
- }
- }
-
private static class ConcreteImsStatusPreferenceController
extends AbstractImsStatusPreferenceController {
- public ConcreteImsStatusPreferenceController(Context context,
+ private ConcreteImsStatusPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index 1d957c3..76a26d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -35,11 +34,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class IpAddressPreferenceControllerTest {
@Mock
private Context mContext;
@@ -75,8 +75,7 @@
private static class ConcreteIpAddressPreferenceController extends
AbstractIpAddressPreferenceController {
- public ConcreteIpAddressPreferenceController(Context context,
- Lifecycle lifecycle) {
+ private ConcreteIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
super(context, lifecycle);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
index dc77400..5b71bdd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -25,16 +25,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SerialNumberPreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
index eb77cb6..5252c6c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
@@ -24,17 +24,16 @@
import android.os.UserManager;
import android.util.SparseBooleanArray;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
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 org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class})
public class SimStatusImeiInfoPreferenceControllerTest {
@@ -106,7 +105,7 @@
private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
- public void setNetworkSupported(int networkType, boolean supported) {
+ private void setNetworkSupported(int networkType, boolean supported) {
mSupportedNetworkTypes.put(networkType, supported);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
index 2e0348d..f09879b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -28,7 +28,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -36,9 +35,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class UptimePreferenceControllerTest {
@Mock
private Context mContext;
@@ -92,7 +92,7 @@
private static class ConcreteUptimePreferenceController
extends AbstractUptimePreferenceController {
- public ConcreteUptimePreferenceController(Context context,
+ private ConcreteUptimePreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 359ea77..74e5bf5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -33,7 +33,6 @@
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -41,13 +40,14 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.List;
@SuppressLint("HardwareIds")
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class WifiMacAddressPreferenceControllerTest {
@Mock
private Lifecycle mLifecycle;
@@ -197,7 +197,7 @@
private static class ConcreteWifiMacAddressPreferenceController
extends AbstractWifiMacAddressPreferenceController {
- public ConcreteWifiMacAddressPreferenceController(Context context,
+ private ConcreteWifiMacAddressPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
index ca621ca..c0924d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
@@ -20,12 +20,11 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BrightnessUtilsTest {
private static final int MIN = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 59a3dd6..605c861 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -20,14 +20,13 @@
import android.util.ArraySet;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Set;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CategoryKeyTest {
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 40e7386..b77670b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -13,15 +13,14 @@
import android.os.Bundle;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TileTest {
private ActivityInfo mActivityInfo;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 362ae4c..bbb4249 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -54,7 +54,6 @@
import android.util.Pair;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -62,12 +61,13 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TileUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
index d0b6dab..2988905 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -33,28 +33,26 @@
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BatterySaverUtilsTest {
- final int BATTERY_SAVER_THRESHOLD_1 = 15;
- final int BATTERY_SAVER_THRESHOLD_2 = 20;
+ private static final int BATTERY_SAVER_THRESHOLD_1 = 15;
+ private static final int BATTERY_SAVER_THRESHOLD_2 = 20;
@Mock
- Context mMockContext;
+ private Context mMockContext;
@Mock
- ContentResolver mMockResolver;
+ private ContentResolver mMockResolver;
@Mock
- PowerManager mMockPowerManager;
+ private PowerManager mMockPowerManager;
@Before
public void setUp() throws Exception {
@@ -66,11 +64,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_firstCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_firstCall_needWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(false, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isFalse();
verify(mMockContext, times(1)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(0)).setPowerSaveMode(anyBoolean());
@@ -83,11 +81,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_secondCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_secondCall_needWarning() {
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -97,11 +95,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_thridCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_thridCall_needWarning() {
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1);
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -111,11 +109,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_firstCall_noWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_firstCall_noWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, false));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -125,12 +123,12 @@
}
@Test
- public void testSetPowerSaveMode_disable_firstCall_noWarning() throws Exception {
+ public void testSetPowerSaveMode_disable_firstCall_noWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, false));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, false)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -141,12 +139,12 @@
}
@Test
- public void testSetPowerSaveMode_disable_firstCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_disable_firstCall_needWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -157,7 +155,7 @@
}
@Test
- public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception {
+ public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() {
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
@@ -172,7 +170,7 @@
}
@Test
- public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() throws Exception {
+ public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() {
Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index 9b1fe5f..bbf807d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManager;
import android.os.IDeviceIdleController;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager;
import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
@@ -40,12 +39,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
public class PowerWhitelistBackendTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 49dde0e..35743c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -16,8 +16,8 @@
package com.android.settingslib.graph;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -25,17 +25,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BatteryMeterDrawableBaseTest {
private static final int CRITICAL_LEVEL = 5;
private static final int PADDING = 5;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 5dbb5ca..1b350cc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -22,14 +22,14 @@
import android.graphics.drawable.VectorDrawable;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothDeviceLayerDrawableTest {
private static final int RES_ID = R.drawable.ic_bt_cellphone;
private static final int BATTERY_LEVEL = 15;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index fa64afe..b930aa6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -25,26 +25,21 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilCompatTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
- HashSet<String> hashSet = new HashSet<>();
- for (String s : strings) {
- hashSet.add(s);
- }
- return hashSet;
+ return new HashSet<>(Arrays.asList(strings));
}
@Test
@@ -105,7 +100,6 @@
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 03ab261..84606b4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -25,26 +25,21 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
- HashSet<String> hashSet = new HashSet<>();
- for (String s : strings) {
- hashSet.add(s);
- }
- return hashSet;
+ return new HashSet<>(Arrays.asList(strings));
}
@Test
@@ -103,7 +98,6 @@
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index b00476b2..4b5e909 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -18,10 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
@@ -32,7 +31,7 @@
import java.util.HashMap;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LicenseHtmlGeneratorFromXmlTest {
private static final String VALILD_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@@ -92,8 +91,8 @@
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
@@ -107,8 +106,8 @@
@Test(expected = XmlPullParserException.class)
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
@@ -117,8 +116,8 @@
@Test
public void testGenerateHtml() {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
@@ -132,8 +131,8 @@
@Test
public void testGenerateHtmlWithCustomHeading() {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index c90de5f..e82bc06 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -20,14 +20,13 @@
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -37,7 +36,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class)
public class LicenseHtmlLoaderCompatTest {
@@ -58,7 +57,7 @@
@Test
public void testLoadInBackground() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -69,7 +68,7 @@
@Test
public void testLoadInBackgroundWithNoVaildXmlFiles() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
File cachedHtmlFile = new File("test.html");
setupFakeData(xmlFiles, cachedHtmlFile, true, true);
@@ -79,7 +78,7 @@
@Test
public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -90,7 +89,7 @@
@Test
public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -112,10 +111,10 @@
@Implements(LicenseHtmlLoaderCompat.class)
public static class ShadowLicenseHtmlLoaderCompat {
- public static List<File> sValidXmlFiles;
- public static File sCachedHtmlFile;
- public static boolean sIsCachedHtmlFileOutdated;
- public static boolean sGenerateHtmlFileSucceeded;
+ private static List<File> sValidXmlFiles;
+ private static File sCachedHtmlFile;
+ private static boolean sIsCachedHtmlFileOutdated;
+ private static boolean sGenerateHtmlFileSucceeded;
@Resetter
public static void reset() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
index c29481f..8c2e899 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
@@ -18,12 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public final class InjectedSettingTest {
private static final String TEST_STRING = "test";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 9c168f7..08d5367 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -2,7 +2,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.isA;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -17,20 +17,19 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RecentLocationAppsTest {
private static final int TEST_UID = 1234;
@@ -56,8 +55,6 @@
private int mTestUserId;
private RecentLocationApps mRecentLocationApps;
-
-
@Before
public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this);
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 50044f2..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,23 +34,19 @@
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 com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DataUsageControllerTest {
private static final String SUB_ID = "Test Subscriber";
@@ -62,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)
@@ -80,78 +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);
@@ -159,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/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 0a03631..011f234 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -27,15 +27,14 @@
import android.os.RemoteException;
import android.text.format.DateUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleChartDataLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 2314f27..d915963 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -19,7 +19,7 @@
import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -33,15 +33,14 @@
import android.net.NetworkPolicyManager;
import android.text.format.DateUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataForUidLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 9d60a97..2d8ea12 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,8 +16,8 @@
package com.android.settingslib.net;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.nullable;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -37,20 +37,19 @@
import android.text.format.DateUtils;
import android.util.Range;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.util.ReflectionHelpers;
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
index 89c319a..59d5674 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
@@ -16,11 +16,10 @@
package com.android.settingslib.notification;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -38,16 +37,15 @@
import android.service.notification.Condition;
import android.view.LayoutInflater;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class EnableZenModeDialogTest {
private EnableZenModeDialog mController;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
index 8147656..437c0d4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
@@ -25,27 +25,23 @@
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
-import android.service.notification.Condition;
import android.view.LayoutInflater;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ZenDurationDialogTest {
private ZenDurationDialog mController;
private Context mContext;
private LayoutInflater mLayoutInflater;
- private Condition mCountdownCondition;
- private Condition mAlarmCondition;
private ContentResolver mContentResolver;
private AlertDialog.Builder mBuilder;
@@ -102,7 +98,6 @@
ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
}
-
@Test
public void testChooseAlwaysPromptSetting() {
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
index 449451a..ffaa7443 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
@@ -31,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.loader.app.LoaderManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -40,10 +39,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSuggestionController.class)
public class SuggestionControllerMixinCompatTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
index aac582f..4dc80f4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
@@ -31,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -40,10 +39,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSuggestionController.class)
public class SuggestionControllerMixinTest {
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 f4afdb1..3e91641 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
@@ -26,19 +26,19 @@
@Implements(DefaultDialerManager.class)
public class ShadowDefaultDialerManager {
- private static String sDefaultDailer;
+ private static String sDefaultDialer;
@Resetter
public void reset() {
- sDefaultDailer = null;
+ sDefaultDialer = null;
}
@Implementation
public static String getDefaultDialerApplication(Context context) {
- return sDefaultDailer;
+ return sDefaultDialer;
}
public static void setDefaultDialerApplication(String dialer) {
- sDefaultDailer = dialer;
+ sDefaultDialer = dialer;
}
}
\ 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 4705cd2..9a169d2 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
@@ -27,7 +27,6 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowActivityManager;
import org.junit.After;
@@ -36,6 +35,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -45,7 +45,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = { ShadowActivityManager.class, UserManagerHelperRoboTest.ShadowUserHandle.class})
public class UserManagerHelperRoboTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
index 645dfa1..026ad47 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
@@ -30,14 +30,13 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class IconCacheTest {
private Icon mIcon;
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 6a9579b..7ef31df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -22,31 +22,30 @@
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.time.Duration;
import java.util.regex.Pattern;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class PowerUtilTest {
- public static final String TEST_BATTERY_LEVEL_10 = "10%";
- public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
- public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
- public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
- public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
- public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
- public static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
- public static final String ENHANCED_SUFFIX = " based on your usage";
+ private static final String TEST_BATTERY_LEVEL_10 = "10%";
+ private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+ private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+ private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+ private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+ private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
+ private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
+ private static final String ENHANCED_SUFFIX = " based on your usage";
// matches a time (ex: '1:15 PM', '2 AM', '23:00')
- public static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
+ private static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
// matches a percentage with parenthesis (ex: '(10%)')
- public static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
+ private static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
private Context mContext;
@@ -108,7 +107,6 @@
+ "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
}
-
@Test
public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index e4bbbcb..8fbbfbb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -25,14 +25,13 @@
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class StringUtilTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 1e066b1..26db124 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -15,28 +15,22 @@
*/
package com.android.settingslib.utils;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ThreadUtilsTest {
@Test
public void testMainThread() throws InterruptedException {
assertThat(ThreadUtils.isMainThread()).isTrue();
- Thread background = new Thread(new Runnable() {
- public void run() {
- assertThat(ThreadUtils.isMainThread()).isFalse();
- }
- });
+ Thread background = new Thread(() -> assertThat(ThreadUtils.isMainThread()).isFalse());
background.start();
background.join();
}
@@ -44,13 +38,11 @@
@Test
public void testEnsureMainThread() throws InterruptedException {
ThreadUtils.ensureMainThread();
- Thread background = new Thread(new Runnable() {
- public void run() {
- try {
- ThreadUtils.ensureMainThread();
- fail("Should not pass ensureMainThread in a background thread");
- } catch (RuntimeException e) {
- }
+ Thread background = new Thread(() -> {
+ try {
+ ThreadUtils.ensureMainThread();
+ fail("Should not pass ensureMainThread in a background thread");
+ } catch (RuntimeException expected) {
}
});
background.start();
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
new file mode 100644
index 0000000..88fef08
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ActionButtonsPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private ActionButtonsPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new ActionButtonsPreference(mContext);
+ }
+
+ @Test
+ public void onBindViewHolder_setTitle_shouldShowButtonByDefault() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setIcon_shouldShowButtonByDefault() {
+ mPref.setButton1Icon(R.drawable.ic_plus);
+ mPref.setButton2Icon(R.drawable.ic_plus);
+ mPref.setButton3Icon(R.drawable.ic_plus);
+ mPref.setButton4Icon(R.drawable.ic_plus);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_notSetTitleOrIcon_shouldNotShowButtonByDefault() {
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibleIsGoneAndSetTitle_shouldNotShowButton() {
+ mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibleIsGoneAndSetIcon_shouldNotShowButton() {
+ mPref.setButton1Icon(R.drawable.ic_plus).setButton1Visible(false);
+ mPref.setButton2Icon(R.drawable.ic_plus).setButton2Visible(false);
+ mPref.setButton3Icon(R.drawable.ic_plus).setButton3Visible(false);
+ mPref.setButton4Icon(R.drawable.ic_plus).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibility_shouldUpdateButtonVisibility() {
+ mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+
+ mPref.setButton1Visible(true);
+ mPref.setButton2Visible(true);
+ mPref.setButton3Visible(true);
+ mPref.setButton4Visible(true);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setEnabled_shouldEnableButton() {
+ mPref.setButton1Enabled(true);
+ mPref.setButton2Enabled(false);
+ mPref.setButton3Enabled(true);
+ mPref.setButton4Enabled(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).isEnabled()).isTrue();
+ assertThat(mRootView.findViewById(R.id.button2).isEnabled()).isFalse();
+ assertThat(mRootView.findViewById(R.id.button3).isEnabled()).isTrue();
+ assertThat(mRootView.findViewById(R.id.button4).isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_setText_shouldShowSameText() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(((Button) mRootView.findViewById(R.id.button1)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button2)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button3)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button4)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ }
+
+ @Test
+ public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(R.drawable.ic_plus);
+
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNotNull();
+ }
+
+ @Test
+ public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(0);
+
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNull();
+ }
+
+ @Test
+ public void setButtonIcon_iconResourceIdNotExisting_shouldNotDisplayIconAndCrash() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(999999999 /* not existing id */);
+ // Should not crash here
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNull();
+ }
+
+ public static ActionButtonsPreference createMock() {
+ final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
+ when(pref.setButton1Text(anyInt())).thenReturn(pref);
+ when(pref.setButton1Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton2Text(anyInt())).thenReturn(pref);
+ when(pref.setButton2Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton3Text(anyInt())).thenReturn(pref);
+ when(pref.setButton3Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton4Text(anyInt())).thenReturn(pref);
+ when(pref.setButton4Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+ return pref;
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index a00f12d..d41d511 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -22,14 +22,13 @@
import android.graphics.drawable.AnimatedRotateDrawable;
import android.view.View;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AnimatedImageViewTest {
private AnimatedImageView mAnimatedImageView;
@@ -47,5 +46,4 @@
AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable();
assertThat(drawable.isRunning()).isTrue();
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
index e030005..601da051 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,9 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceMixinCompatTest {
@Mock
@@ -97,5 +97,4 @@
verify(mScreen).removePreference(any(FooterPreference.class));
verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 8817ff7..7ae5d2d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,10 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowApplication;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceMixinTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index e0eceb4..0d2399e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -26,14 +26,14 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
new file mode 100644
index 0000000..99261a3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class LayoutPreferenceTest {
+
+ private LayoutPreference mPreference;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ final Context mContext = RuntimeEnvironment.application;
+ mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+ mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
+ .inflate(R.layout.layout_preference_frame, null, false));
+ }
+
+ @Test
+ public void setOnPreferenceClickListener_layoutPreferenceShouldListenClickEvent() {
+ final OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
+
+ mPreference.setOnPreferenceClickListener(listener);
+ mPreference.onBindViewHolder(mHolder);
+
+ mHolder.itemView.callOnClick();
+
+ verify(listener).onPreferenceClick(mPreference);
+ assertThat(mHolder.itemView.isFocusable()).isTrue();
+ assertThat(mHolder.itemView.isClickable()).isTrue();
+ }
+
+ @Test
+ public void setNonSelectable_viewShouldNotBeSelectable() {
+ mPreference.setSelectable(false);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.itemView.isFocusable()).isFalse();
+ assertThat(mHolder.itemView.isClickable()).isFalse();
+ }
+
+ @Test
+ public void disableSomeView_shouldMaintainStateAfterBind() {
+ mPreference.findViewById(android.R.id.button1).setEnabled(false);
+ mPreference.findViewById(android.R.id.button2).setEnabled(true);
+
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mPreference.findViewById(android.R.id.button1).isEnabled()).isFalse();
+ assertThat(mPreference.findViewById(android.R.id.button2).isEnabled()).isTrue();
+ }
+
+ @Test
+ public void allowDividerBelow_shouldSaveCorrectDividerStatus() {
+ mPreference.setAllowDividerBelow(true);
+
+ assertThat(mPreference.isAllowDividerBelow()).isTrue();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
index 10c9dfb..da97cc8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
@@ -23,14 +23,13 @@
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AppPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 86443bd..c5cbea7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -25,16 +25,15 @@
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AccessPointPreferenceTest {
private Context mContext = RuntimeEnvironment.application;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
index f0e8c66..b059df1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
@@ -22,15 +22,14 @@
import android.net.WifiKey;
import android.os.Parcel;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Date;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TimestampedScoredNetworkTest {
private TimestampedScoredNetwork impl;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 07c50fd..89960cb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -37,19 +37,19 @@
import android.util.ArraySet;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Set;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class WifiUtilsTest {
private static final String TEST_SSID = "\"test_ssid\"";
private static final String TEST_BSSID = "00:00:00:00:00:00";
@@ -79,7 +79,7 @@
Bundle bundle = new Bundle();
ArrayList<ScanResult> scanResults = buildScanResultCache();
bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS,
- scanResults.toArray(new Parcelable[scanResults.size()]));
+ scanResults.toArray(new Parcelable[0]));
AccessPoint ap = new AccessPoint(mContext, bundle);
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cbb6e82..5e7fb85 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -418,6 +418,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,
@@ -855,6 +861,10 @@
GlobalSettingsProto.MultiSim.SMS_PROMPT);
p.end(multiSimToken);
+ dumpSetting(s, p,
+ Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
+ GlobalSettingsProto.NATIVE_FLAGS_HEALTH_CHECK_ENABLED);
+
final long netstatsToken = p.start(GlobalSettingsProto.NETSTATS);
dumpSetting(s, p,
Settings.Global.NETSTATS_ENABLED,
@@ -994,6 +1004,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,
@@ -1302,6 +1315,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);
@@ -1910,6 +1926,15 @@
SecureSettingsProto.Location.CHANGER);
p.end(locationToken);
+ final long locationAccessCheckToken = p.start(SecureSettingsProto.LOCATION_ACCESS_CHECK);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+ SecureSettingsProto.LocationAccessCheck.INTERVAL_MILLIS);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS,
+ SecureSettingsProto.LocationAccessCheck.DELAY_MILLIS);
+ p.end(locationAccessCheckToken);
+
final long lockScreenToken = p.start(SecureSettingsProto.LOCK_SCREEN);
// Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
// Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 00ea45c..140a5a3 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";
@@ -414,9 +416,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, null, makeDefault, requestingUserId, false);
break;
}
@@ -444,8 +445,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(requestingUserId, mode, prefix);
break;
}
@@ -463,15 +464,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, requestingUserId, false) ? 1 : 0;
Bundle result = new Bundle();
result.putInt(RESULT_ROWS_DELETED, rows);
return result;
@@ -484,10 +478,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 +514,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;
}
@@ -1061,36 +1077,47 @@
MUTATION_OPERATION_INSERT, forceNotify, 0);
}
- private void resetConfigSetting(int requestingUserId, int mode, String tag) {
+ private boolean deleteConfigSetting(String name, int requestingUserId, boolean forceNotify) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ", " + requestingUserId
+ + ", " + forceNotify + ")");
+ }
+ return mutateConfigSetting(name, null, null, false, requestingUserId,
+ MUTATION_OPERATION_DELETE, forceNotify, 0);
+ }
+
+ private void resetConfigSetting(int requestingUserId, int mode, String prefix) {
if (DEBUG) {
Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
- + mode + ", " + tag + ")");
+ + mode + ", " + prefix + ")");
}
- mutateConfigSetting(null, null, tag, false, requestingUserId,
+ mutateConfigSetting(null, null, prefix, false, requestingUserId,
MUTATION_OPERATION_RESET, false, mode);
}
- private boolean mutateConfigSetting(String name, String value, String tag,
+ private boolean mutateConfigSetting(String name, String value, String prefix,
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
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,
+ UserHandle.USER_SYSTEM, name, value, null, makeDefault,
getCallingPackage(), forceNotify, null);
}
+ case MUTATION_OPERATION_DELETE: {
+ return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM, name, forceNotify, 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 +1125,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 +2140,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);
}
@@ -2644,6 +2706,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 +2723,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 +2744,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 +2764,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 +2787,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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cb55231..e564711 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -130,6 +130,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/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/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index f8b61cd..44354bc1 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -302,11 +302,13 @@
}
/**
- * Returns whether there is a soft nav bar.
+ * Returns whether there is a soft nav bar on specified display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
*/
- public boolean hasSoftNavigationBar() {
+ public boolean hasSoftNavigationBar(int displayId) {
try {
- return mIwm.hasNavigationBar();
+ return mIwm.hasNavigationBar(displayId);
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
index dfa38ba..8723fb9 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
@@ -631,7 +631,8 @@
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, getHandler(), true /* scaleUp */);
+ future, animStartedListener, getHandler(), true /* scaleUp */,
+ getContext().getDisplayId());
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
event.task.getTopComponent().flattenToShortString());
} else {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index f492208..4891e50 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -30,6 +30,12 @@
int VERSION = 1;
void startPendingIntentDismissingKeyguard(PendingIntent intent);
+
+ /**
+ * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but
+ * allow you to specify the callback that is executed after the intent is sent.
+ */
+ void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback);
void startActivity(Intent intent, boolean dismissShade);
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index ba4eb5f..88b8dd8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -52,7 +52,9 @@
}
class Sensor {
- public static int TYPE_WAKE_LOCK_SCREEN = 1;
+ public static final int TYPE_WAKE_LOCK_SCREEN = 1;
+ public static final int TYPE_WAKE_DISPLAY = 2;
+ public static final int TYPE_SWIPE = 3;
int mType;
@@ -68,6 +70,7 @@
class TriggerEvent {
Sensor mSensor;
int mVendorType;
+ float[] mValues;
/**
* Creates a trigger event
@@ -76,14 +79,30 @@
* e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
*/
public TriggerEvent(Sensor sensor, int vendorType) {
+ this(sensor, vendorType, null);
+ }
+
+ /**
+ * Creates a trigger event
+ * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
+ * @param vendorType The vendor type, which should be unique for each type of sensor,
+ * e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
+ * @param values Values captured by the sensor.
+ */
+ public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
mSensor = sensor;
mVendorType = vendorType;
+ mValues = values;
}
public Sensor getSensor() {
return mSensor;
}
+ public float[] getValues() {
+ return mValues;
+ }
+
public int getVendorType() {
return mVendorType;
}
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/drawable/ic_notification_block.xml b/packages/SystemUI/res/drawable/ic_notification_block.xml
index 572e97b..27690740 100644
--- a/packages/SystemUI/res/drawable/ic_notification_block.xml
+++ b/packages/SystemUI/res/drawable/ic_notification_block.xml
@@ -20,6 +20,6 @@
android:viewportHeight="24.0">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="#FF000000"
android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
</vector>
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 5ca34b0..1e8cd5a 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -160,6 +160,15 @@
android:maxLines="2"
android:text="@string/biometric_dialog_confirm"
android:visibility="gone"/>
+ <!-- Try Again Button -->
+ <Button android:id="@+id/button_try_again"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="gone"/>
<Space android:id="@+id/rightSpacer"
android:layout_width="12dip"
android:layout_height="match_parent"
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/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index c86ebe7..d502baa 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -51,7 +51,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgicon" />
<TextView
- android:id="@+id/pkg_group_divider"
+ android:id="@+id/pkg_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -61,7 +61,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgname" />
<TextView
- android:id="@+id/group_name"
+ android:id="@+id/delegate_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -70,7 +70,7 @@
android:ellipsize="end"
android:maxLines="1"
android:layout_centerVertical="true"
- android:layout_toEndOf="@id/pkg_group_divider" />
+ android:layout_toEndOf="@id/pkg_divider" />
<!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
<ImageButton
android:id="@+id/info"
@@ -101,13 +101,39 @@
android:layout_marginStart="@*android:dimen/notification_content_margin_start"
android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
android:orientation="vertical">
- <!-- Channel Name -->
- <TextView
- android:id="@+id/channel_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- style="@android:style/TextAppearance.Material.Notification.Title" />
+ <RelativeLayout
+ android:id="@+id/names"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_centerVertical="true" />
+ <TextView
+ android:id="@+id/pkg_group_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:text="@*android:string/notification_header_divider_symbol"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/group_name" />
+ <!-- Channel Name -->
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@android:style/TextAppearance.Material.Notification.Title"
+ android:layout_toEndOf="@id/pkg_group_divider"/>
+ </RelativeLayout>
<!-- Question prompt -->
<TextView
android:id="@+id/block_prompt"
@@ -118,6 +144,7 @@
<!-- Settings and Done buttons -->
<LinearLayout
+ android:id="@+id/block_or_minimize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_guts_button_spacing"
@@ -152,13 +179,6 @@
android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
style="@style/TextAppearance.NotificationInfo.Button" />
<TextView
- android:id="@+id/toggle_silent"
- android:text="@string/inline_silent_button_silent"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
- style="@style/TextAppearance.NotificationInfo.Button" />
- <TextView
android:id="@+id/keep"
android:minWidth="48dp"
android:text="@string/inline_keep_button"
@@ -167,6 +187,44 @@
android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
style="@style/TextAppearance.NotificationInfo.Button"/>
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/interruptiveness_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_guts_button_spacing"
+ android:layout_marginStart="@dimen/notification_guts_button_side_margin"
+ android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/int_block"
+ android:text="@string/inline_block_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:drawableTop="@drawable/ic_notification_block"
+ android:drawableTint="?android:attr/colorAccent"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/int_silent"
+ android:text="@string/inline_minimize_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:drawableTop="@drawable/ic_notifications_silence"
+ android:drawableTint="?android:attr/colorAccent"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/int_alert"
+ android:text="@string/inline_keep_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:drawableTop="@drawable/ic_notifications_alert"
+ android:drawableTint="?android:attr/colorAccent"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </LinearLayout>
</LinearLayout>
<com.android.systemui.statusbar.notification.row.NotificationUndoLayout
android:id="@+id/confirmation"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index 2f7d486..bc15f2c4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -26,9 +26,7 @@
android:id="@+id/dialog_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="@dimen/ongoing_appops_dialog_content_padding">
-
+ android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
@@ -37,21 +35,29 @@
android:textDirection="locale"
android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
android:textColor="@*android:color/text_color_primary"
- android:paddingStart="@dimen/ongoing_appops_dialog_title_padding"
- android:paddingEnd="@dimen/ongoing_appops_dialog_title_padding"
- android:paddingBottom="@dimen/ongoing_appops_dialog_sep"
+ android:layout_marginStart="@dimen/ongoing_appops_dialog_title_margin_sides"
+ android:layout_marginEnd="@dimen/ongoing_appops_dialog_title_margin_sides"
+ android:layout_marginBottom="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
+ android:layout_marginTop="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
/>
<LinearLayout
- android:id="@+id/items_container"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="start"
- />
+ android:layout_marginBottom="@dimen/ongoing_appops_dialog_items_bottom_margin" >
- <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
- android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/items_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="start"
+ />
+
+ <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
+ android:visibility="gone" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
index f05f7ba..ecfbfb4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -17,37 +17,39 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/ongoing_appops_dialog_line_height"
+ android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
+ android:layout_marginEnd="@dimen/ongoing_appops_dialog_text_padding"
android:fillViewport="true"
android:orientation="horizontal"
- android:layout_marginTop="@dimen/ongoing_appops_dialog_text_margin"
- android:focusable="true" >
+ android:focusable="true"
+ android:layout_gravity="center_vertical">
<ImageView
android:id="@+id/app_icon"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
- android:layout_width="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
+ android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
+ android:layout_gravity="start|center_vertical"
/>
<TextView
android:id="@+id/app_name"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
- android:gravity="bottom|start"
+ android:gravity="start|center_vertical"
android:textDirection="locale"
android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
android:textColor="@*android:color/text_color_primary"
- android:paddingStart="@dimen/ongoing_appops_dialog_text_padding"
- android:paddingEnd="@dimen/ongoing_appops_dialog_text_padding"
-
+ android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
/>
<LinearLayout
android:id="@+id/icons"
- android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+ android:layout_height="match_parent"
android:layout_width="wrap_content"
android:gravity="end"
+ android:layout_gravity="end|center_vertical"
android:visibility="gone"
/>
</LinearLayout>
\ No newline at end of file
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..7ea4027 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/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="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..4f5b23e 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>
@@ -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..5e7a119 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>
@@ -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/config.xml b/packages/SystemUI/res/values/config.xml
index d8648fa..9e97cd8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -464,4 +464,6 @@
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
</string-array>
+
+ <integer name="ongoing_appops_dialog_max_apps">5</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0997c5b1..8e0bfb6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,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
@@ -945,14 +946,20 @@
<dimen name="ongoing_appops_dialog_sep">16dp</dimen>
<!--Padding around text items in Ongoing App Ops dialog -->
<dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
- <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
- <dimen name="ongoing_appops_dialog_icon_height">28dp</dimen>
- <!-- Margin between text lines in Ongoing App Ops dialog -->
- <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
- <!-- Side padding of title in Ongoing App Ops dialog -->
- <dimen name="ongoing_appops_dialog_title_padding">10dp</dimen>
- <!-- Padding around Ongoing App Ops dialog content -->
- <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+ <!-- Height and width of App Opp icons in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen>
+ <!-- Left margin of App Opp icons in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
+ <!-- Height and width of Application icons in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
+ <!-- Height of line in Ongoing App Ops dialog-->
+ <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
+ <!-- Side margin of title in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_title_margin_sides">24dp</dimen>
+ <!-- Bottom margin of items in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
+ <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
<!-- Side margins around the Ongoing App Ops chip-->
<dimen name="ongoing_appops_chip_margin">12dp</dimen>
<!-- Top and bottom margins around the Ongoing App Ops chip -->
@@ -968,9 +975,9 @@
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
<!-- Text size for Ongoing App Ops dialog title -->
- <dimen name="ongoing_appops_dialog_title_size">24sp</dimen>
+ <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
<!-- Text size for Ongoing App Ops dialog items -->
- <dimen name="ongoing_appops_dialog_item_size">20sp</dimen>
+ <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
<!-- How much a bubble is elevated -->
<dimen name="bubble_elevation">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4a0bc9b..9917257 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -272,6 +272,8 @@
<string name="accessibility_biometric_dialog_help_area">Help message area</string>
<!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_confirm">Confirm</string>
+ <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] -->
+ <string name="biometric_dialog_try_again">Try again</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
@@ -700,6 +702,8 @@
<string name="quick_settings_bluetooth_secondary_label_headset">Headset</string>
<!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_input">Input</string>
+ <!-- QuickSettings: Bluetooth secondary label for a Hearing Aids device being connected [CHAR LIMIT=20]-->
+ <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing Aids</string>
<!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
@@ -1556,17 +1560,26 @@
<!-- Notification inline controls: block notifications button -->
<string name="inline_stop_button">Stop notifications</string>
+ <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+ <string name="inline_block_button">Block</string>
+
<!-- Notification inline controls: keep getting notifications button -->
<string name="inline_keep_button">Keep showing</string>
<!-- Notification inline controls: minimize notifications button -->
<string name="inline_minimize_button">Minimize</string>
- <!-- Notification inline controls: show notifications silently button [CHAR_LIMIT=25] -->
+ <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
<string name="inline_silent_button_silent">Show silently</string>
- <!-- Notification inline controls: show and alert button [CHAR_LIMIT=25] -->
- <string name="inline_silent_button_alert">Show and alert</string>
+ <!-- Notification inline controls: button to continue showing notifications silently [CHAR_LIMIT=35] -->
+ <string name="inline_silent_button_stay_silent">Stay silent</string>
+
+ <!-- Notification inline controls: button to make notifications alert the user [CHAR_LIMIT=35] -->
+ <string name="inline_silent_button_alert">Alert me</string>
+
+ <!-- Notification inline controls: button to continue alerting the user when notifications arrive [CHAR_LIMIT=35] -->
+ <string name="inline_silent_button_keep_alerting">Keep alerting</string>
<!-- Notification Inline controls: continue receiving notifications prompt, app level -->
<string name="inline_keep_showing_app">Keep showing notifications from this app?</string>
@@ -1574,6 +1587,9 @@
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
+ <!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
+ <string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
<string name="appops_camera">This app is using the camera.</string>
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
@@ -2251,7 +2267,11 @@
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
<!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
- <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+ <plurals name="ongoing_privacy_chip_multiple_apps">
+ <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> app</item>
+ <item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+ </plurals>
<!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
<string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
@@ -2260,12 +2280,16 @@
<string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
<!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
- <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</string>
+ <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+ <item quantity="one"><xliff:g id="num_apps" example="1">%1$d</xliff:g> application is using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+ <item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+ </plurals>
<!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
<string name="ongoing_privacy_dialog_cancel">Cancel</string>
- <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+ <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
<string name="ongoing_privacy_dialog_open_settings">View details</string>
<!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
@@ -2292,6 +2316,6 @@
<!-- Text for indicating extra apps using app ops [CHAR LIMIT=NONE] -->
<plurals name="ongoing_privacy_dialog_overflow_text">
<item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
- <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other app</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e9aa1b6..fede934 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -262,7 +262,7 @@
<style name="TextAppearance.AppOpsDialog.Item">
<item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">sans-serif</item>
</style>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
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/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index ece2bb9..f3bdbae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -71,4 +71,9 @@
*/
void onStatusBarMotionEvent(in MotionEvent event) = 9;
+ /**
+ * Get the corner radius of windows in pixels.
+ */
+ float getWindowCornerRadius() = 10;
+
}
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 65c5220..a9cf857 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
@@ -83,6 +83,7 @@
t.setWindowCrop(params.surface, params.windowCrop);
t.setAlpha(params.surface, params.alpha);
t.setLayer(params.surface, params.layer);
+ t.setCornerRadius(params.surface, params.cornerRadius);
t.show(params.surface);
}
@@ -98,12 +99,13 @@
* @param windowCrop Crop to apply.
*/
public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
- Rect windowCrop, int layer) {
+ Rect windowCrop, int layer, float cornerRadius) {
this.surface = surface.mSurfaceControl;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
this.windowCrop = new Rect(windowCrop);
this.layer = layer;
+ this.cornerRadius = cornerRadius;
}
final SurfaceControl surface;
@@ -111,5 +113,6 @@
final Matrix matrix;
final Rect windowCrop;
final int layer;
+ final float cornerRadius;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 70258c2..2aba3fa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -20,7 +20,6 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
public class TransactionCompat {
@@ -53,7 +52,7 @@
}
public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
- mTransaction.setSize(surfaceControl.mSurfaceControl, w, h);
+ mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);
return this;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 42e60aa..8a251ae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -18,6 +18,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -61,7 +62,7 @@
public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
- public static final int NAV_BAR_POS_INVALID = -1;
+ public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID;
public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
@@ -100,23 +101,23 @@
* Overrides a pending app transition.
*/
public void overridePendingAppTransitionMultiThumbFuture(
- AppTransitionAnimationSpecsFuture animationSpecFuture,
- Runnable animStartedCallback, Handler animStartedCallbackHandler, boolean scaleUp) {
+ AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
+ Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
try {
WindowManagerGlobal.getWindowManagerService()
.overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(),
RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
- animStartedCallback), scaleUp);
+ animStartedCallback), scaleUp, displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
}
}
public void overridePendingAppTransitionRemote(
- RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+ RemoteAnimationAdapterCompat remoteAnimationAdapter, int displayId) {
try {
WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
- remoteAnimationAdapter.getWrapped());
+ remoteAnimationAdapter.getWrapped(), displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to override pending app transition (remote): ", e);
}
@@ -159,11 +160,13 @@
}
/**
- * @return whether there is a soft nav bar.
+ * @param displayId the id of display to check if there is a software navigation bar.
+ *
+ * @return whether there is a soft nav bar on specific display.
*/
- public boolean hasSoftNavigationBar() {
+ public boolean hasSoftNavigationBar(int displayId) {
try {
- return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
+ return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(displayId);
} catch (RemoteException e) {
return false;
}
@@ -176,10 +179,9 @@
* @see #NAV_BAR_POS_BOTTOM
* @see #NAV_BAR_POS_INVALID
*/
- public int getNavBarPosition() {
+ public int getNavBarPosition(int displayId) {
try {
- // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
- return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to get nav bar position");
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6b0a7a9..904f944 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1531,10 +1531,11 @@
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
- if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
+ boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+ || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+ if (runningOrRestarting && !shouldListenForFingerprint) {
stopListeningForFingerprint();
- } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
- && shouldListenForFingerprint) {
+ } else if (!runningOrRestarting && shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
@@ -1589,6 +1590,10 @@
setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ // Waiting for restart via handleFingerprintError().
+ return;
+ }
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = ActivityManager.getCurrentUser();
if (isUnlockWithFingerprintPossible(userId)) {
@@ -1899,13 +1904,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);
@@ -1920,7 +1930,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) {
@@ -2418,6 +2428,8 @@
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
pw.println(" possible=" + isUnlockWithFingerprintPossible(userId));
+ pw.println(" listening: actual=" + mFingerprintRunningState
+ + " expected=" + (shouldListenForFingerprint() ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index e1b8dc8..9e7c5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -36,6 +36,15 @@
}
@Override
+ public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+ Runnable intentSentCallback) {
+ if (mActualStarter == null) {
+ return;
+ }
+ mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback);
+ }
+
+ @Override
public void startActivity(Intent intent, boolean dismissShade) {
if (mActualStarter == null) {
return;
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 c0047c0..ba89fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -21,7 +21,7 @@
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -52,15 +52,20 @@
private static final int MSG_BUTTON_NEGATIVE = 6;
private static final int MSG_USER_CANCELED = 7;
private static final int MSG_BUTTON_POSITIVE = 8;
+ private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9;
+ private static final int MSG_TRY_AGAIN_PRESSED = 10;
private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
private SomeArgs mCurrentDialogArgs;
private BiometricDialogView mCurrentDialog;
private WindowManager mWindowManager;
- private IBiometricPromptReceiver mReceiver;
+ private IBiometricServiceReceiverInternal mReceiver;
private boolean mDialogShowing;
private Callback mCallback = new Callback();
+ private boolean mTryAgainShowing; // No good place to save state before config change :/
+ private boolean mConfirmShowing; // No good place to save state before config change :/
+
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -89,6 +94,15 @@
case MSG_BUTTON_POSITIVE:
handleButtonPositive();
break;
+ case MSG_BIOMETRIC_SHOW_TRY_AGAIN:
+ handleShowTryAgain();
+ break;
+ case MSG_TRY_AGAIN_PRESSED:
+ handleTryAgainPressed();
+ break;
+ default:
+ Log.w(TAG, "Unknown message: " + msg.what);
+ break;
}
}
};
@@ -96,7 +110,7 @@
private class Callback implements DialogViewCallback {
@Override
public void onUserCanceled() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+ mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
}
@Override
@@ -107,12 +121,17 @@
@Override
public void onNegativePressed() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+ mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
}
@Override
public void onPositivePressed() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+ mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
}
}
@@ -121,7 +140,7 @@
createDialogs();
if (!mDialogs.isEmpty()) {
- getComponent(CommandQueue.class).addCallbacks(this);
+ getComponent(CommandQueue.class).addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
}
@@ -139,13 +158,14 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
// Remove these messages as they are part of the previous client
mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
mHandler.removeMessages(MSG_BIOMETRIC_HELP);
mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
+ mHandler.removeMessages(MSG_HIDE_DIALOG);
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
@@ -179,6 +199,12 @@
mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
}
+ @Override
+ public void showBiometricTryAgain() {
+ if (DEBUG) Log.d(TAG, "showBiometricTryAgain");
+ mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget();
+ }
+
private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
mCurrentDialogArgs = args;
final int type = args.argi1;
@@ -193,11 +219,13 @@
Log.w(TAG, "Dialog already showing");
return;
}
- mReceiver = (IBiometricPromptReceiver) args.arg2;
+ mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
mCurrentDialog.setBundle((Bundle)args.arg1);
mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
mCurrentDialog.setUserId(args.argi2);
mCurrentDialog.setSkipIntro(skipAnimation);
+ mCurrentDialog.setPendingTryAgain(mTryAgainShowing);
+ mCurrentDialog.setPendingConfirm(mConfirmShowing);
mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
mDialogShowing = true;
}
@@ -209,7 +237,8 @@
mContext.getResources()
.getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
if (mCurrentDialog.requiresConfirmation()) {
- mCurrentDialog.showConfirmationButton();
+ mConfirmShowing = true;
+ mCurrentDialog.showConfirmationButton(true /* show */);
} else {
handleHideDialog(false /* userCanceled */);
}
@@ -226,6 +255,7 @@
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
return;
}
+ mTryAgainShowing = false;
mCurrentDialog.showErrorMessage(error);
}
@@ -246,6 +276,8 @@
}
mReceiver = null;
mDialogShowing = false;
+ mConfirmShowing = false;
+ mTryAgainShowing = false;
mCurrentDialog.startDismiss();
}
@@ -259,6 +291,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Remote exception when handling negative button", e);
}
+ mTryAgainShowing = false;
handleHideDialog(false /* userCanceled */);
}
@@ -272,13 +305,31 @@
} catch (RemoteException e) {
Log.e(TAG, "Remote exception when handling positive button", e);
}
+ mConfirmShowing = false;
handleHideDialog(false /* userCanceled */);
}
private void handleUserCanceled() {
+ mTryAgainShowing = false;
+ mConfirmShowing = false;
handleHideDialog(true /* userCanceled */);
}
+ private void handleShowTryAgain() {
+ mCurrentDialog.showTryAgainButton(true /* show */);
+ mTryAgainShowing = true;
+ }
+
+ private void handleTryAgainPressed() {
+ try {
+ mCurrentDialog.clearTemporaryMessage();
+ mTryAgainShowing = false;
+ mReceiver.onTryAgainPressed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when handling try again", e);
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 38427ad..e085f23 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -87,6 +87,9 @@
protected boolean mRequireConfirmation;
private int mUserId; // used to determine if we should show work background
+ private boolean mPendingShowTryAgain;
+ private boolean mPendingShowConfirm;
+
protected abstract void updateIcon(int lastState, int newState);
protected abstract int getHintStringResourceId();
protected abstract int getAuthenticatedAccessibilityResourceId();
@@ -178,6 +181,7 @@
final Button negative = mLayout.findViewById(R.id.button2);
final Button positive = mLayout.findViewById(R.id.button1);
final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
+ final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
@@ -193,6 +197,11 @@
mCallback.onPositivePressed();
});
+ tryAgain.setOnClickListener((View v) -> {
+ showTryAgainButton(false /* show */);
+ mCallback.onTryAgainPressed();
+ });
+
mLayout.setFocusableInTouchMode(true);
mLayout.requestFocus();
}
@@ -207,7 +216,6 @@
final TextView subtitle = mLayout.findViewById(R.id.subtitle);
final TextView description = mLayout.findViewById(R.id.description);
final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
final ImageView backgroundView = mLayout.findViewById(R.id.background);
if (mUserManager.isManagedProfile(mUserId)) {
@@ -233,8 +241,6 @@
title.setText(titleText);
title.setSelected(true);
- positive.setVisibility(View.INVISIBLE);
-
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
if (TextUtils.isEmpty(subtitleText)) {
subtitle.setVisibility(View.GONE);
@@ -243,7 +249,8 @@
subtitle.setText(subtitleText);
}
- final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+ final CharSequence descriptionText =
+ mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
if (TextUtils.isEmpty(descriptionText)) {
description.setVisibility(View.GONE);
} else {
@@ -253,6 +260,9 @@
negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+ showTryAgainButton(mPendingShowTryAgain);
+ showConfirmationButton(mPendingShowConfirm);
+
if (mWasForceRemoved || mSkipIntro) {
// Show the dialog immediately
mLayout.animate().cancel();
@@ -281,11 +291,17 @@
public void startDismiss() {
mAnimatingAway = true;
+ // This is where final cleanup should occur.
final Runnable endActionRunnable = new Runnable() {
@Override
public void run() {
mWindowManager.removeView(BiometricDialogView.this);
mAnimatingAway = false;
+ // Set the icons / text back to normal state
+ handleClearMessage();
+ showTryAgainButton(false /* show */);
+ mPendingShowTryAgain = false;
+ mPendingShowConfirm = false;
}
};
@@ -345,9 +361,13 @@
return mRequireConfirmation;
}
- public void showConfirmationButton() {
+ public void showConfirmationButton(boolean show) {
final Button positive = mLayout.findViewById(R.id.button1);
- positive.setVisibility(View.VISIBLE);
+ if (show) {
+ positive.setVisibility(View.VISIBLE);
+ } else {
+ positive.setVisibility(View.GONE);
+ }
}
public void setUserId(int userId) {
@@ -376,12 +396,18 @@
BiometricPrompt.HIDE_DIALOG_DELAY);
}
+ public void clearTemporaryMessage() {
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+ mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget();
+ }
+
public void showHelpMessage(String message) {
showTemporaryMessage(message);
}
public void showErrorMessage(String error) {
showTemporaryMessage(error);
+ showTryAgainButton(false /* show */);
mCallback.onErrorShown();
}
@@ -390,6 +416,25 @@
mLastState = newState;
}
+ public void showTryAgainButton(boolean show) {
+ final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+ if (show) {
+ tryAgain.setVisibility(View.VISIBLE);
+ } else {
+ tryAgain.setVisibility(View.GONE);
+ }
+ }
+
+ // Set the state before the window is attached, so we know if the dialog should be started
+ // with or without the button. This is because there's no good onPause signal
+ public void setPendingTryAgain(boolean show) {
+ mPendingShowTryAgain = show;
+ }
+
+ public void setPendingConfirm(boolean show) {
+ mPendingShowConfirm = show;
+ }
+
public WindowManager.LayoutParams getLayoutParams() {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index f388d9c..24fd22e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -43,4 +43,9 @@
* should be dismissed.
*/
void onPositivePressed();
+
+ /**
+ * Invoked when the "try again" button is pressed.
+ */
+ void onTryAgainPressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e868f96..1e91ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,15 +23,16 @@
import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
import android.app.Notification;
-import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.view.ViewGroup;
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;
@@ -57,6 +58,11 @@
// When a bubble is dismissed, recreate it as a notification
public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
+ // Secure settings
+ private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
+ private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
+ private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+
private Context mContext;
private BubbleDismissListener mDismissListener;
private BubbleStateChangeListener mStateChangeListener;
@@ -67,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();
@@ -268,7 +275,7 @@
for (BubbleView bv : mBubbles.values()) {
NotificationData.Entry entry = bv.getEntry();
if (entry != null) {
- if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+ if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
viewsToRemove.add(bv);
}
}
@@ -297,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.
@@ -318,11 +330,15 @@
/**
* Whether the notification should bubble or not.
*/
- public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority,
- boolean canAppOverlay) {
- if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) {
+ public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+ if (entry.isBubbleDismissed()) {
return false;
}
+
+ boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+ boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+ boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+
StatusBarNotification n = entry.notification;
boolean hasRemoteInput = false;
if (n.getNotification().actions != null) {
@@ -333,12 +349,31 @@
}
}
}
+ 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 shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH
- || Notification.MessagingStyle.class.equals(style)
- || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
- || hasRemoteInput
- || canAppOverlay;
- return shouldBubble && !entry.isBubbleDismissed();
+ boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category);
+ boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
+ return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages)
+ || (isImportantOngoing && autoBubbleOngoing)
+ || autoBubbleAll;
+ }
+
+ private static boolean shouldAutoBubbleMessages(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0;
+ }
+
+ private static boolean shouldAutoBubbleOngoing(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0;
+ }
+
+ private static boolean shouldAutoBubbleAll(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index a79e047..6c47aac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -120,7 +120,7 @@
* @return the view to display when the bubble is expanded.
*/
public ExpandableNotificationRow getRowView() {
- return mEntry.row;
+ return mEntry.getRow();
}
@Override
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/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 21b21d9..eda3c59 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -177,9 +177,22 @@
log("state " + state);
}
- public static void traceWakeLockScreenWakeUp() {
+ /**
+ * Appends lock screen wake up event to the logs.
+ * @param wake if we're waking up or sleeping.
+ */
+ public static void traceLockScreenWakeUp(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp");
+ log("wakeLockScreenWakeUp " + wake);
+ }
+
+ /**
+ * Appends wake-display event to the logs.
+ * @param wake if we're waking up or sleeping.
+ */
+ public static void traceWakeDisplay(boolean wake) {
+ if (!ENABLED) return;
+ log("wakeLockScreenWakeUp " + wake);
}
public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 01a2345..1dd3101 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -24,20 +24,21 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.os.Build;
import android.os.Handler;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
/**
* Controls the screen brightness when dozing.
*/
public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
SensorEventListener {
+ private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
+ .getBoolean("debug.aod_brightness", false);
protected static final String ACTION_AOD_BRIGHTNESS =
"com.android.systemui.doze.AOD_BRIGHTNESS";
protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
@@ -83,11 +84,9 @@
mSensorToScrimOpacity = sensorToScrimOpacity;
if (mDebuggable) {
- Dependency.get(Dependency.BG_HANDLER).post(()-> {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_AOD_BRIGHTNESS);
- mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
- });
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_AOD_BRIGHTNESS);
+ mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
}
}
@@ -97,7 +96,7 @@
this(context, service, sensorManager, lightSensor, host, handler,
context.getResources().getInteger(
com.android.internal.R.integer.config_screenBrightnessDoze),
- policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE);
+ policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
}
@Override
@@ -126,9 +125,7 @@
private void onDestroy() {
setLightSensorEnabled(false);
if (mDebuggable) {
- Dependency.get(Dependency.BG_HANDLER).post(()-> {
- mContext.unregisterReceiver(this);
- });
+ mContext.unregisterReceiver(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 77f7ad4f..7e77843 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,7 @@
package com.android.systemui.doze;
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
import android.annotation.AnyThread;
@@ -67,7 +68,6 @@
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
- private final Consumer<Boolean> mWakeScreenCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
@@ -76,8 +76,7 @@
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
- Callback callback, Consumer<Boolean> proxCallback,
- Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
+ Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -85,7 +84,6 @@
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
- mWakeScreenCallback = wakeScreenCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -123,7 +121,13 @@
DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */),
- new WakeScreenSensor(),
+ new PluginTriggerSensor(
+ new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
+ Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
+ true /* configured */,
+ DozeLog.REASON_SENSOR_WAKE_UP,
+ false /* reports touch coordinates */,
+ false /* touchscreen */),
};
mProxSensor = new ProxSensor(policy);
@@ -395,8 +399,11 @@
screenX = event.values[0];
screenY = event.values[1];
}
- mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY);
- updateListener(); // reregister, this sensor only fires once
+ mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
+ event.values);
+ if (!mRegistered) {
+ updateListener(); // reregister, this sensor only fires once
+ }
}));
}
@@ -429,7 +436,16 @@
private final SensorManagerPlugin.Sensor mPluginSensor;
private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
- onTrigger(null);
+ DozeLog.traceSensor(mContext, mPulseReason);
+ mHandler.post(mWakeLock.wrap(() -> {
+ if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+ mRegistered = false;
+ mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+ event.getValues());
+ if (!mRegistered) {
+ updateListener(); // reregister, this sensor only fires once
+ }
+ }));
};
PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
@@ -463,27 +479,17 @@
.append(", mSensor=").append(mPluginSensor).append("}").toString();
}
- }
-
- private class WakeScreenSensor extends TriggerSensor {
-
- WakeScreenSensor() {
- super(findSensorWithType(mConfig.wakeScreenSensorType()),
- Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
- DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
- false /* requiresTouchscreen */);
- }
-
- @Override
- @AnyThread
- public void onTrigger(TriggerEvent event) {
- DozeLog.traceSensor(mContext, mPulseReason);
- mHandler.post(mWakeLock.wrap(() -> {
- if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
- mRegistered = false;
- mWakeScreenCallback.accept(event.values[0] > 0);
- updateListener(); // reregister, this sensor only fires once
- }));
+ private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+ if (event == null) return null;
+ final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
+ .append(event.getSensor()).append(',')
+ .append(event.getVendorType());
+ if (event.getValues() != null) {
+ for (int i = 0; i < event.getValues().length; i++) {
+ sb.append(',').append(event.getValues()[i]);
+ }
+ }
+ return sb.append(']').toString();
}
}
@@ -494,11 +500,11 @@
* @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
* @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
* @param screenX the location on the screen where the sensor fired or -1
- * if the sensor doesn't support reporting screen locations.
+ * if the sensor doesn't support reporting screen locations.
* @param screenY the location on the screen where the sensor fired or -1
- * if the sensor doesn't support reporting screen locations.
+ * @param rawValues raw values array from the event.
*/
void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck,
- float screenX, float screenY);
+ float screenX, float screenY, float[] rawValues);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index d69b1bf..afe9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
+ config, wakeLock, this::onSensor, this::onProximityFar,
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -124,13 +124,17 @@
}
private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
- float screenX, float screenY) {
+ float screenX, float screenY, float[] rawValues) {
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+ boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
- if (isLongPress) {
+ if (isWakeDisplay) {
+ onWakeScreen(wakeEvent);
+ } else if (isLongPress) {
requestPulse(pulseReason, sensorPerformedProxCheck);
} else {
proximityCheckThenCall((result) -> {
@@ -141,7 +145,15 @@
if (isDoubleTap) {
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
- } else if (isPickup || isWakeLockScreen) {
+ } else if (isWakeLockScreen) {
+ if (wakeEvent) {
+ mDozeHost.setPassiveInterrupt(true);
+ mMachine.wakeUp();
+ DozeLog.traceLockScreenWakeUp(wakeEvent);
+ } else {
+ if (DEBUG) Log.d(TAG, "Unpulsing");
+ }
+ } else if (isPickup) {
mDozeHost.setPassiveInterrupt(true);
mMachine.wakeUp();
} else {
@@ -157,8 +169,6 @@
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
- } else if (isWakeLockScreen) {
- DozeLog.traceWakeLockScreenWakeUp();
}
}
@@ -174,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);
@@ -184,6 +195,7 @@
}
private void onWakeScreen(boolean wake) {
+ DozeLog.traceWakeDisplay(wake);
DozeMachine.State state = mMachine.getState();
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
@@ -194,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/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 9a5a5b8..be504ef 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -23,6 +23,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
@@ -80,11 +81,12 @@
if (isAmbientMode != mIsAmbientMode) {
mIsAmbientMode = isAmbientMode;
try {
+ long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
if (DEBUG) {
Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
- + ", animated: " + animated);
+ + ", animationDuration: " + duration);
}
- mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
+ mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
} catch (RemoteException e) {
// Cannot notify wallpaper manager service, but it's fine, let's just skip it.
Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
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/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e78951a..201c7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -175,7 +174,7 @@
* @param builder The slice builder.
*/
protected void addZenMode(ListBuilder builder) {
- if (!isDndSuppressingNotifications()) {
+ if (!isDndOn()) {
return;
}
RowBuilder dndBuilder = new RowBuilder(mDndUri)
@@ -187,13 +186,10 @@
}
/**
- * Return true if DND is enabled suppressing notifications.
+ * Return true if DND is enabled.
*/
- protected boolean isDndSuppressingNotifications() {
- boolean suppressingNotifications = (mZenModeController.getConfig().suppressedVisualEffects
- & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
- return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF
- && suppressingNotifications;
+ protected boolean isDndOn() {
+ return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
}
@Override
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/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index e447def..8495fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -19,6 +19,8 @@
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
@@ -65,6 +67,7 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -90,8 +93,8 @@
public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
public static final int MESSAGE_ANIMATION_ENDED = 6;
- private static final long INITIAL_DISMISS_DELAY = 3500;
- private static final long POST_INTERACTION_DISMISS_DELAY = 2000;
+ private static final int INITIAL_DISMISS_DELAY = 3500;
+ private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
private static final long MENU_FADE_DURATION = 125;
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
@@ -105,6 +108,7 @@
private final List<RemoteAction> mActions = new ArrayList<>();
+ private AccessibilityManager mAccessibilityManager;
private View mViewRoot;
private Drawable mBackgroundDrawable;
private View mMenuContainer;
@@ -194,6 +198,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.pip_menu_activity);
+ mAccessibilityManager = getSystemService(AccessibilityManager.class);
mBackgroundDrawable = new ColorDrawable(Color.BLACK);
mBackgroundDrawable.setAlpha(0);
mViewRoot = findViewById(R.id.background);
@@ -639,8 +644,10 @@
mHandler.removeCallbacks(mFinishRunnable);
}
- private void repostDelayedFinish(long delay) {
+ private void repostDelayedFinish(int delay) {
+ int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+ FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
mHandler.removeCallbacks(mFinishRunnable);
- mHandler.postDelayed(mFinishRunnable, delay);
+ mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 03a573e..6a9f24c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -368,7 +368,7 @@
private void onAccessibilityShowMenu() {
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
+ mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
}
private boolean handleTouchEvent(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 5d33ffd..f054345 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -411,13 +411,8 @@
setNextLogTime();
- // This initialization method may be called on a configuration change. Only one set of
- // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
- // schedule an ongoing callback.
- mHandler.removeCallbacks(mUpdateTempCallback);
-
// We have passed all of the checks, start checking the temp
- updateTemperatureWarning();
+ mHandler.post(mUpdateTempCallback);
}
private void showThermalShutdownDialog() {
@@ -448,6 +443,8 @@
logTemperatureStats();
+ // Remove any pending callbacks as we only want to enable one
+ mHandler.removeCallbacks(mUpdateTempCallback);
mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
}
@@ -553,11 +550,7 @@
// Thermal event received from vendor thermal management subsystem
private final class ThermalEventListener extends IThermalEventListener.Stub {
@Override public void notifyThrottling(Temperature temp) {
- // Trigger an update of the temperature warning. Only one
- // callback can be enabled at a time, so remove any existing
- // callback; updateTemperatureWarning will schedule another one.
- mHandler.removeCallbacks(mUpdateTempCallback);
- updateTemperatureWarning();
+ mHandler.post(mUpdateTempCallback);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index d3715d0..65ed889 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -78,8 +78,9 @@
if (builder.app != null) {
text.setText(builder.app?.applicationName)
} else {
- text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
- builder.appsAndTypes.size)
+ text.text = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_chip_multiple_apps,
+ builder.appsAndTypes.size, builder.appsAndTypes.size)
}
}
} else {
@@ -100,9 +101,9 @@
context.getString(R.string.ongoing_privacy_chip_content_single_app,
builder.app?.applicationName, typesText)
} else {
- contentDescription = context.getString(
- R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
- builder.appsAndTypes.size, typesText)
+ contentDescription = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_chip_content_multiple_apps_single_op,
+ builder.appsAndTypes.size, builder.appsAndTypes.size, typesText)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index f6a95af..bbdae29 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@
import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
@@ -34,13 +35,13 @@
val dialogBuilder: PrivacyDialogBuilder
) {
- val iconSize = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_dialog_icon_height)
- val iconColor = context.resources.getColor(
+ private val iconSize = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_dialog_icon_size)
+ private val iconColor = context.resources.getColor(
com.android.internal.R.color.text_color_primary, context.theme)
- companion object {
- private const val MAX_ITEMS = 10
- }
+ private val iconMargin = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_dialog_icon_margin)
+ private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
fun createDialog(): Dialog {
val builder = AlertDialog.Builder(context).apply {
@@ -105,6 +106,11 @@
val appName = item.findViewById(R.id.app_name) as TextView
val icons = item.findViewById(R.id.icons) as LinearLayout
+ var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
+ gravity = Gravity.CENTER_VERTICAL
+ marginStart = iconMargin
+ }
+
app.icon?.let {
appIcon.setImageDrawable(it)
}
@@ -117,7 +123,7 @@
imageTintList = ColorStateList.valueOf(iconColor)
setImageDrawable(it)
}
- icons.addView(image, iconSize, LinearLayout.LayoutParams.WRAP_CONTENT)
+ icons.addView(image, lp)
}
icons.visibility = View.VISIBLE
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 3fa3e8e..268462e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -18,10 +18,14 @@
import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dependency
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
@@ -33,25 +37,29 @@
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
+ val intents = listOf(Intent.ACTION_USER_FOREGROUND,
+ Intent.ACTION_MANAGED_PROFILE_ADDED,
+ Intent.ACTION_MANAGED_PROFILE_REMOVED)
+ const val TAG = "PrivacyItemController"
}
private var privacyList = emptyList<PrivacyItem>()
private val appOpsController = Dependency.get(AppOpsController::class.java)
private val userManager = context.getSystemService(UserManager::class.java)
- private val currentUser = ActivityManager.getCurrentUser()
- private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ private var currentUserIds = emptyList<Int>()
private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
+ private var listening = false
+
private val notifyChanges = Runnable {
callback.privacyChanged(privacyList)
}
+
private val updateListAndNotifyChanges = Runnable {
updatePrivacyList()
uiHandler.post(notifyChanges)
}
- private var listening = false
-
private val cb = object : AppOpsController.Callback {
override fun onActiveStateChanged(
code: Int,
@@ -61,12 +69,36 @@
) {
val userId = UserHandle.getUserId(uid)
if (userId in currentUserIds) {
- update()
+ update(false)
}
}
}
- private fun update() {
+ @VisibleForTesting
+ internal var userSwitcherReceiver = Receiver()
+ set(value) {
+ context.unregisterReceiver(field)
+ field = value
+ registerReceiver()
+ }
+
+ init {
+ registerReceiver()
+ }
+
+ private fun registerReceiver() {
+ context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
+ intents.forEach {
+ addAction(it)
+ }
+ }, null, null)
+ }
+
+ private fun update(updateUsers: Boolean) {
+ if (updateUsers) {
+ val currentUser = ActivityManager.getCurrentUser()
+ currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ }
bgHandler.post(updateListAndNotifyChanges)
}
@@ -75,7 +107,7 @@
listening = listen
if (listening) {
appOpsController.addCallback(OPS, cb)
- update()
+ update(true)
} else {
appOpsController.removeCallback(OPS, cb)
}
@@ -102,4 +134,12 @@
interface Callback {
fun privacyChanged(privacyItems: List<PrivacyItem>)
}
+
+ internal inner class Receiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action in intents) {
+ update(true)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 8b434a5..496aa0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -323,7 +323,9 @@
post(new Runnable() {
@Override
public void run() {
- handleShowingDetail(detail, x, y, false /* toggleQs */);
+ if (isAttachedToWindow()) {
+ handleShowingDetail(detail, x, y, false /* toggleQs */);
+ }
}
});
}
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/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 591e9e0..9d2be39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -26,6 +26,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
+import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -69,7 +70,8 @@
mSpecs.clear();
mFinished = false;
// Enqueue jobs to fetch every system tile and then ever package tile.
- addStockTiles(host);
+ addCurrentAndStockTiles(host);
+
addPackageTiles(host);
}
@@ -77,16 +79,28 @@
return mFinished;
}
- private void addStockTiles(QSTileHost host) {
- String possible = mContext.getString(R.string.quick_settings_tiles_stock);
+ private void addCurrentAndStockTiles(QSTileHost host) {
+ String stock = mContext.getString(R.string.quick_settings_tiles_stock);
+ String current = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.QS_TILES);
final ArrayList<String> possibleTiles = new ArrayList<>();
- possibleTiles.addAll(Arrays.asList(possible.split(",")));
- if (Build.IS_DEBUGGABLE) {
+ if (current != null) {
+ // The setting QS_TILES is not populated immediately upon Factory Reset
+ possibleTiles.addAll(Arrays.asList(current.split(",")));
+ }
+ String[] stockSplit = stock.split(",");
+ for (String spec : stockSplit) {
+ if (!current.contains(spec)) {
+ possibleTiles.add(spec);
+ }
+ }
+ if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) {
possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
for (String spec : possibleTiles) {
+ // Only add current and stock tiles that can be created from QSFactoryImpl
final QSTile tile = host.createTile(spec);
if (tile == null) {
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index b2f6043..7d52f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -114,7 +114,7 @@
}
// Broken tiles.
- Log.w(TAG, "Bad tile spec: " + tileSpec);
+ Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
}
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 0638998..3a96595d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -198,7 +198,8 @@
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
- mAccessibilityClass = state.expandedAccessibilityClassName;
+ mAccessibilityClass =
+ state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index c62a592..3ab1c21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -205,7 +205,10 @@
} else {
final BluetoothClass bluetoothClass = lastDevice.getBtClass();
if (bluetoothClass != null) {
- if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+ if (lastDevice.isHearingAidDevice()) {
+ return mContext.getString(
+ R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
+ } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_audio);
} else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b89324..12b6f67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -43,6 +43,7 @@
import android.util.Log;
import android.view.MotionEvent;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
@@ -97,6 +98,7 @@
private int mCurrentBoundedUserId = -1;
private float mBackButtonAlpha;
private MotionEvent mStatusBarGestureDownEvent;
+ private float mWindowCornerRadius;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -228,6 +230,18 @@
}
}
+ public float getWindowCornerRadius() {
+ if (!verifyCaller("getWindowCornerRadius")) {
+ return 0;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ return mWindowCornerRadius;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -334,6 +348,7 @@
.setPackage(mRecentsComponentName.getPackageName());
mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
getDefaultInteractionFlags());
+ mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
// Listen for the package update changes.
if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
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/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 216b940..f796793 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -219,7 +219,7 @@
mLayout.findViewById(R.id.screen_pinning_text_area)
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
- if (WindowManagerWrapper.getInstance().hasSoftNavigationBar()) {
+ if (WindowManagerWrapper.getInstance().hasSoftNavigationBar(mContext.getDisplayId())) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 3da6d2e..bc38169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -112,8 +112,7 @@
return;
}
- alertEntry.mEntry.row.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ alertEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
if (alert) {
alertEntry.updateEntry(true /* updatePostTime */);
}
@@ -186,7 +185,7 @@
alertEntry.setEntry(entry);
mAlertEntries.put(entry.key, alertEntry);
onAlertEntryAdded(alertEntry);
- entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
/**
@@ -207,7 +206,7 @@
Entry entry = alertEntry.mEntry;
mAlertEntries.remove(key);
onAlertEntryRemoved(alertEntry);
- entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
alertEntry.reset();
if (mExtendedLifetimeAlertEntries.contains(entry)) {
if (mNotificationLifetimeFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index f1c0304..8821679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -33,7 +33,7 @@
* dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly
* before automatically dismissing the alert.
*/
-public final class AmbientPulseManager extends AlertingNotificationManager {
+public class AmbientPulseManager extends AlertingNotificationManager {
protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
@VisibleForTesting
@@ -79,7 +79,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setAmbientPulsing(true);
+ entry.setAmbientPulsing(true);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, true);
}
@@ -88,11 +88,11 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setAmbientPulsing(false);
+ entry.setAmbientPulsing(false);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, false);
}
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0c8f487..95019ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,7 +21,7 @@
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -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;
@@ -96,6 +98,7 @@
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_TRY_AGAIN = 47 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -163,12 +166,13 @@
default void onRotationProposal(int rotation, boolean isValid) { }
- default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
+ default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int type, boolean requireConfirmation, int userId) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
default void hideBiometricDialog() { }
+ default void showBiometricTryAgain() { }
}
@VisibleForTesting
@@ -181,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);
}
@@ -221,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);
}
@@ -264,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.
@@ -280,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,
@@ -288,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);
@@ -369,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();
@@ -383,7 +397,9 @@
}
}
- public void appTransitionPending() {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionPending(int displayId) {
appTransitionPending(false /* forced */);
}
@@ -393,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 */);
}
@@ -410,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);
}
@@ -523,8 +544,8 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -565,6 +586,13 @@
}
}
+ @Override
+ public void showBiometricTryAgain() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -774,7 +802,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).showBiometricDialog(
(Bundle) someArgs.arg1,
- (IBiometricPromptReceiver) someArgs.arg2,
+ (IBiometricServiceReceiverInternal) someArgs.arg2,
someArgs.argi1 /* type */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */);
@@ -816,6 +844,11 @@
mCallbacks.get(i).showPinningEscapeToast();
}
break;
+ case MSG_BIOMETRIC_TRY_AGAIN:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showBiometricTryAgain();
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
index 3b611a3..2f26109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
@@ -22,8 +22,11 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManagerGlobal;
@@ -34,6 +37,8 @@
*/
public class DisplayNavigationBarController implements DisplayListener {
+ private static final String TAG = DisplayNavigationBarController.class.getName();
+
private final Context mContext;
private final Handler mHandler;
private final DisplayManager mDisplayManager;
@@ -112,14 +117,23 @@
}
final int displayId = display.getDisplayId();
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ if (!wms.hasNavigationBar(displayId)) {
+ return;
+ }
+ } catch (RemoteException e) {
+ // Cannot get wms, just return with warning message.
+ Log.w(TAG, "Cannot get WindowManager.");
+ return;
+ }
final Context externalDisplayContext = mContext.createDisplayContext(display);
- NavigationBarFragment.create(externalDisplayContext,
- (tag, fragment) -> {
- final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
- // TODO(b/115978725): handle external nav bars sysuiVisibility
- navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- mExternalNavigationBarMap.append(displayId, navBar);
- }
- );
+ NavigationBarFragment.create(externalDisplayContext, (tag, fragment) -> {
+ final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+ // TODO(b/115978725): handle external nav bars sysuiVisibility
+ navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ mExternalNavigationBarMap.append(displayId, navBar);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 745b2f9..b9684fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -27,7 +27,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
public class EmptyShadeView extends StackScrollerDecorView {
@@ -74,7 +73,7 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ public ExpandableViewState createExpandableViewState() {
return new EmptyShadeViewState();
}
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/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fc1e94a..f045548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -199,7 +199,7 @@
for (int i = 0; i < N; i++) {
final NotificationData.Entry entry = activeNotifications.get(i);
- if (isMediaNotification(entry)) {
+ if (entry.isMediaNotification()) {
final MediaSession.Token token =
entry.notification.getNotification().extras.getParcelable(
Notification.EXTRA_MEDIA_SESSION);
@@ -336,13 +336,6 @@
return PlaybackState.STATE_NONE;
}
- private boolean isMediaNotification(NotificationData.Entry entry) {
- // TODO: confirm that there's a valid media key
- return entry.row.getExpandedContentView() != null
- && entry.row.getExpandedContentView().findViewById(
- com.android.internal.R.id.media_actions) != null;
- }
-
private void clearCurrentMediaNotificationSession() {
mMediaMetadata = null;
if (mMediaController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 0e9f950..3334f8b 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,8 +65,7 @@
int getMaxNotificationsWhileLocked(boolean recompute);
/**
- * True if the presenter
- * @return
+ * True if the presenter is currently locked.
*/
default boolean isPresenterLocked() { return false; }
@@ -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 8c53cc2..ba69f3b 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);
@@ -150,34 +149,42 @@
}
private void logActionClick(View view) {
+ Integer actionIndex = (Integer)
+ view.getTag(com.android.internal.R.id.notification_action_index_tag);
+ if (actionIndex == null) {
+ Log.e(TAG, "Couldn't retrieve the actionIndex from the clicked button");
+ return;
+ }
ViewParent parent = view.getParent();
- String key = getNotificationKeyForParent(parent);
- if (key == null) {
+ StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+ if (statusBarNotification == null) {
Log.w(TAG, "Couldn't determine notification for click.");
return;
}
- int index = -1;
+ String key = statusBarNotification.getKey();
+ int buttonIndex = -1;
// If this is a default template, determine the index of the button.
if (view.getId() == com.android.internal.R.id.action0 &&
parent != null && parent instanceof ViewGroup) {
ViewGroup actionGroup = (ViewGroup) parent;
- index = actionGroup.indexOfChild(view);
+ buttonIndex = actionGroup.indexOfChild(view);
}
final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
final int rank = mEntryManager.getNotificationData().getRank(key);
+ final Notification.Action action =
+ statusBarNotification.getNotification().actions[actionIndex];
final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true);
try {
- mBarService.onNotificationActionClick(key, index, nv);
+ mBarService.onNotificationActionClick(key, buttonIndex, action, nv, false);
} catch (RemoteException e) {
// Ignore
}
}
- private String getNotificationKeyForParent(ViewParent parent) {
+ private StatusBarNotification getNotificationForParent(ViewParent parent) {
while (parent != null) {
if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent)
- .getStatusBarNotification().getKey();
+ return ((ExpandableNotificationRow) parent).getStatusBarNotification();
}
parent = parent.getParent();
}
@@ -312,10 +319,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() {
@@ -393,7 +398,7 @@
}
public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -403,7 +408,7 @@
}
public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -532,7 +537,7 @@
// Ensure the entry hasn't already been removed. This can happen if there is an
// inflation exception while updating the remote history
- if (entry.row == null || entry.row.isRemoved()) {
+ if (entry.isRemoved()) {
return;
}
@@ -570,7 +575,7 @@
mEntryManager.updateNotification(newSbn, null);
- if (entry.row == null || entry.row.isRemoved()) {
+ if (entry.isRemoved()) {
return;
}
@@ -593,7 +598,7 @@
protected class RemoteInputActiveExtender extends RemoteInputExtender {
@Override
public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
return mRemoteInputController.isRemoteInputActive(entry);
@@ -658,11 +663,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 37cc299..6cec36a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -47,7 +47,6 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -68,7 +67,6 @@
private boolean mDark;
private NotificationIconContainer mShelfIcons;
- private ShelfState mShelfState;
private int[] mTmp = new int[2];
private boolean mHideBackground;
private int mIconAppearTopPadding;
@@ -115,7 +113,6 @@
setClipChildren(false);
setClipToPadding(false);
mShelfIcons.setIsStaticLayout(false);
- mShelfState = new ShelfState();
setBottomRoundness(1.0f, false /* animate */);
initDimens();
}
@@ -124,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) {
@@ -187,52 +184,53 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
- return mShelfState;
+ public ExpandableViewState createExpandableViewState() {
+ return new ShelfState();
}
- public void updateState(StackScrollState resultState,
- AmbientState ambientState) {
- View lastView = ambientState.getLastVisibleBackgroundChild();
+ /** Update the state of the shelf. */
+ public void updateState(AmbientState ambientState) {
+ ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
+ ShelfState viewState = (ShelfState) getViewState();
if (mShowNotificationShelf && lastView != null) {
float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ ambientState.getStackTranslation();
- ExpandableViewState lastViewState = resultState.getViewStateForView(lastView);
+ ExpandableViewState lastViewState = lastView.getViewState();
float viewEnd = lastViewState.yTranslation + lastViewState.height;
- mShelfState.copyFrom(lastViewState);
- mShelfState.height = getIntrinsicHeight();
+ viewState.copyFrom(lastViewState);
+ viewState.height = getIntrinsicHeight();
- float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
+ float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
getFullyClosedTranslation());
float darkTranslation = mAmbientState.getDarkTopPadding();
float yRatio = mAmbientState.hasPulsingNotifications() ?
0 : mAmbientState.getDarkAmount();
- mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
- mShelfState.zTranslation = ambientState.getBaseZHeight();
+ viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
+ viewState.zTranslation = ambientState.getBaseZHeight();
// For the small display size, it's not enough to make the icon not covered by
// the top cutout so the denominator add the height of cutout.
// Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
// mAmbientState.getTopPadding().
- float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
+ float openedAmount = (viewState.yTranslation - getFullyClosedTranslation())
/ (getIntrinsicHeight() * 2 + mCutoutHeight);
openedAmount = Math.min(1.0f, openedAmount);
- mShelfState.openedAmount = openedAmount;
- mShelfState.clipTopAmount = 0;
- mShelfState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
- mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
- mShelfState.hideSensitive = false;
- mShelfState.xTranslation = getTranslationX();
+ viewState.openedAmount = openedAmount;
+ viewState.clipTopAmount = 0;
+ viewState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
+ viewState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
+ viewState.hideSensitive = false;
+ viewState.xTranslation = getTranslationX();
if (mNotGoneIndex != -1) {
- mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
+ viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
}
- mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
- mShelfState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing();
- mShelfState.maxShelfEnd = maxShelfEnd;
+ viewState.hasItemsInStableShelf = lastViewState.inShelf;
+ viewState.hidden = !mAmbientState.isShadeExpanded()
+ || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
+ viewState.maxShelfEnd = maxShelfEnd;
} else {
- mShelfState.hidden = true;
- mShelfState.location = ExpandableViewState.LOCATION_GONE;
- mShelfState.hasItemsInStableShelf = false;
+ viewState.hidden = true;
+ viewState.location = ExpandableViewState.LOCATION_GONE;
+ viewState.hasItemsInStableShelf = false;
}
}
@@ -261,7 +259,7 @@
int notGoneIndex = 0;
int colorOfViewBeforeLast = NO_COLOR;
boolean backgroundForceHidden = false;
- if (mHideBackground && !mShelfState.hasItemsInStableShelf) {
+ if (mHideBackground && !((ShelfState) getViewState()).hasItemsInStableShelf) {
backgroundForceHidden = true;
}
int colorTwoBefore = NO_COLOR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ea67736..dc3a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -51,6 +51,7 @@
public class NotificationViewHierarchyManager {
private static final String TAG = "NotificationViewHierarchyManager";
+ //TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -128,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,
@@ -140,6 +141,7 @@
/**
* Updates the visual representation of the notifications.
*/
+ //TODO: Rewrite this to focus on Entries, or some other data object instead of views
public void updateNotificationViews() {
ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
.getActiveNotifications();
@@ -148,12 +150,12 @@
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationData.Entry ent = activeNotifications.get(i);
- if (ent.row.isDismissed() || ent.row.isRemoved()) {
+ if (ent.isRowDismissed() || ent.isRowRemoved()) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
}
- ent.row.setStatusBarState(mStatusBarStateListener.getCurrentState());
+ ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
&& mStatusBarStateListener.getCurrentState() == SHADE;
if (showAsBubble) {
@@ -175,20 +177,19 @@
boolean deviceSensitive = devicePublic
&& !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
mLockscreenUserManager.getCurrentUserId());
- ent.row.setSensitive(sensitive, deviceSensitive);
- ent.row.setNeedsRedaction(needsRedaction);
- if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
- ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
- ent.row.getStatusBarNotification());
+ ent.getRow().setSensitive(sensitive, deviceSensitive);
+ ent.getRow().setNeedsRedaction(needsRedaction);
+ if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
+ NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
List<ExpandableNotificationRow> orderedChildren =
- mTmpChildOrderMap.get(summary);
+ mTmpChildOrderMap.get(summary.getRow());
if (orderedChildren == null) {
orderedChildren = new ArrayList<>();
- mTmpChildOrderMap.put(summary, orderedChildren);
+ mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
}
- orderedChildren.add(ent.row);
+ orderedChildren.add(ent.getRow());
} else {
- toShow.add(ent.row);
+ toShow.add(ent.getRow());
}
}
@@ -391,19 +392,19 @@
&& !row.isLowPriority()));
}
- entry.row.setOnAmbient(getShadeController().isDozing());
+ entry.getRow().setOnAmbient(getShadeController().isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
- entry.notification) && !entry.row.isRemoved();
+ entry.notification) && !entry.isRowRemoved();
boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
.notification);
if (!showOnKeyguard) {
// min priority notifications should show if their summary is showing
if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
- ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+ NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
entry.notification);
if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
- summary.getStatusBarNotification())) {
+ summary.notification)) {
showOnKeyguard = true;
}
}
@@ -411,16 +412,16 @@
if (suppressedSummary
|| mLockscreenUserManager.shouldHideNotifications(userId)
|| (onKeyguard && !showOnKeyguard)) {
- entry.row.setVisibility(View.GONE);
+ entry.getRow().setVisibility(View.GONE);
} else {
- boolean wasGone = entry.row.getVisibility() == View.GONE;
+ boolean wasGone = entry.getRow().getVisibility() == View.GONE;
if (wasGone) {
- entry.row.setVisibility(View.VISIBLE);
+ entry.getRow().setVisibility(View.VISIBLE);
}
- if (!isChildNotification && !entry.row.isRemoved()) {
+ if (!isChildNotification && !entry.getRow().isRemoved()) {
if (wasGone) {
// notify the scroller of a child addition
- mListContainer.generateAddAnimation(entry.row,
+ mListContainer.generateAddAnimation(entry.getRow(),
!showOnKeyguard /* fromMoreCard */);
}
visibleNotifications++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 929f43e..e8abcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -250,16 +250,16 @@
// Make a copy because closing the remote inputs will modify mOpen.
ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
for (int i = mOpen.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = mOpen.get(i).first.get();
- if (item != null && item.row != null) {
- list.add(item);
+ NotificationData.Entry entry = mOpen.get(i).first.get();
+ if (entry != null && entry.rowExists()) {
+ list.add(entry);
}
}
for (int i = list.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = list.get(i);
- if (item.row != null) {
- item.row.closeRemoteInput();
+ NotificationData.Entry entry = list.get(i);
+ if (entry.rowExists()) {
+ entry.closeRemoteInput();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 758c33a..f5d6904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -15,12 +15,15 @@
*/
package com.android.systemui.statusbar;
+import android.app.Notification;
import android.os.RemoteException;
import android.util.ArraySet;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.Set;
@@ -32,6 +35,9 @@
private IStatusBarService mBarService;
private Set<String> mSendingKeys = new ArraySet<>();
private Callback mCallback;
+ private final NotificationEntryManager mEntryManager =
+ Dependency.get(NotificationEntryManager.class);
+
public SmartReplyController() {
mBarService = Dependency.get(IStatusBarService.class);
@@ -41,12 +47,34 @@
mCallback = callback;
}
- public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
+ /**
+ * Notifies StatusBarService a smart reply is sent.
+ */
+ public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+ boolean generatedByAssistant) {
mCallback.onSmartReplySent(entry, reply);
mSendingKeys.add(entry.key);
try {
- mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
- replyIndex);
+ mBarService.onNotificationSmartReplySent(
+ entry.notification.getKey(), replyIndex, reply, generatedByAssistant);
+ } catch (RemoteException e) {
+ // Nothing to do, system going down
+ }
+ }
+
+ /**
+ * Notifies StatusBarService a smart action is clicked.
+ */
+ public void smartActionClicked(
+ NotificationData.Entry entry, int actionIndex, Notification.Action action,
+ boolean generatedByAssistant) {
+ final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
+ final int rank = mEntryManager.getNotificationData().getRank(entry.key);
+ final NotificationVisibility nv =
+ NotificationVisibility.obtain(entry.key, rank, count, true);
+ try {
+ mBarService.onNotificationActionClick(
+ entry.key, actionIndex, action, nv, generatedByAssistant);
} catch (RemoteException e) {
// Nothing to do, system going down
}
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/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f0e5462..f899863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -29,6 +29,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
@@ -55,6 +56,7 @@
private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final StatusBarWindowView mStatusBarWindow;
+ private final float mWindowCornerRadius;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -72,6 +74,8 @@
mNotificationContainer = container;
mStatusBarWindow = statusBarWindow;
mCallback = callback;
+ mWindowCornerRadius = ScreenDecorationsUtils
+ .getWindowCornerRadius(statusBarWindow.getResources());
}
public RemoteAnimationAdapter getLaunchAnimation(
@@ -124,6 +128,8 @@
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
+ private final float mNotificationCornerRadius;
+ private float mCornerRadius;
private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
@@ -131,6 +137,8 @@
mSourceNotification = sourceNofitication;
mParams = new ExpandAnimationParameters();
mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
+ mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
+ mSourceNotification.getCurrentBottomRoundness());
}
@Override
@@ -181,8 +189,7 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mParams.linearProgress = animation.getAnimatedFraction();
- float progress
- = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
mParams.linearProgress);
int newWidth = (int) MathUtils.lerp(notificationWidth,
targetWidth, progress);
@@ -194,6 +201,8 @@
+ notificationHeight,
primary.position.y + primary.sourceContainerBounds.bottom,
progress);
+ mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
+ mWindowCornerRadius, progress);
applyParamsToWindow(primary);
applyParamsToNotification(mParams);
applyParamsToNotificationList(mParams);
@@ -259,7 +268,7 @@
m.postTranslate(0, (float) (mParams.top - app.position.y));
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash),
- 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex);
+ 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius);
mSyncRtTransactionApplier.scheduleApply(params);
}
@@ -290,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/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 314a31d..0a2e04f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -250,23 +250,24 @@
otherChild = null;
}
}
- if (otherChild == null) {
+ if (otherChild == null && previousTranslation < 0) {
+ // Let's fade out as we approach the top of the screen. We can only do this if
+ // we're actually moving up
float distanceToTop = child.getTop() + child.getHeight() + previousTranslation;
transformationAmount = distanceToTop / child.getHeight();
transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount));
- if (to) {
- transformationAmount = 1.0f - transformationAmount;
- }
}
transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
useLinearTransformation);
- if (transformationAmount == 0.0f
- && otherGroup.getIsolatedMessage() == otherChild) {
+ boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild;
+ if (transformationAmount == 0.0f && otherIsIsolated) {
ownGroup.setTransformingImages(true);
}
if (otherChild == null) {
child.setTranslationY(previousTranslation);
setClippingDeactivated(child, true);
+ } else if (ownGroup.getIsolatedMessage() == child || otherIsIsolated) {
+ // We don't want to add any translation for the image that is transforming
} else if (to) {
float totalTranslation = child.getTop() + ownGroup.getTop()
- otherChild.getTop() - otherChild.getTop();
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 da6d977..3f8583c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -64,6 +64,8 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -106,7 +108,6 @@
public int importance;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
- public ExpandableNotificationRow row; // the outer expanded view
private boolean interruption;
public boolean autoRedacted; // whether the redacted notification was generated by us
public int targetSdk;
@@ -119,6 +120,10 @@
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ private Entry parent; // our parent (if we're in a group)
+ private ArrayList<Entry> children = new ArrayList<Entry>();
+ private ExpandableNotificationRow row; // the outer expanded view
+
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
@@ -168,7 +173,6 @@
public void populateFromRanking(@NonNull Ranking ranking) {
channel = ranking.getChannel();
audiblyAlerted = ranking.audiblyAlerted();
- noisy = ranking.isNoisy();
importance = ranking.getImportance();
snoozeCriteria = ranking.getSnoozeCriteria();
userSentiment = ranking.getUserSentiment();
@@ -212,6 +216,24 @@
}
}
+ public ExpandableNotificationRow getRow() {
+ return row;
+ }
+
+ //TODO: This will go away when we have a way to bind an entry to a row
+ public void setRow(ExpandableNotificationRow row) {
+ this.row = row;
+ }
+
+ @Nullable
+ public List<Entry> getChildren() {
+ if (children.size() <= 0) {
+ return null;
+ }
+
+ return children;
+ }
+
public void notifyFullScreenIntentLaunched() {
setInterruption();
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -409,6 +431,198 @@
initializationTime = time;
}
}
+
+ public void sendAccessibilityEvent(int eventType) {
+ if (row != null) {
+ row.sendAccessibilityEvent(eventType);
+ }
+ }
+
+ /**
+ * Used by NotificationMediaManager to determine... things
+ * @return {@code true} if we are a media notification
+ */
+ public boolean isMediaNotification() {
+ if (row == null) return false;
+
+ return row.isMediaRow();
+ }
+
+ /**
+ * We are a top level child if our parent is the list of notifications duh
+ * @return {@code true} if we're a top level notification
+ */
+ public boolean isTopLevelChild() {
+ return row != null && row.isTopLevelChild();
+ }
+
+ public void resetUserExpansion() {
+ if (row != null) row.resetUserExpansion();
+ }
+
+ public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+ if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+ }
+
+ public void setAmbientPulsing(boolean pulsing) {
+ if (row != null) row.setAmbientPulsing(pulsing);
+ }
+
+ public boolean rowExists() {
+ return row != null;
+ }
+
+ public boolean isRowDismissed() {
+ return row != null && row.isDismissed();
+ }
+
+ public boolean isRowRemoved() {
+ return row != null && row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or removed
+ */
+ public boolean isRemoved() {
+ //TODO: recycling invalidates this
+ return row == null || row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or dismissed
+ */
+ public boolean isDismissed() {
+ //TODO: recycling
+ return row == null || row.isDismissed();
+ }
+
+ public boolean isRowPinned() {
+ return row != null && row.isPinned();
+ }
+
+ public void setRowPinned(boolean pinned) {
+ if (row != null) row.setPinned(pinned);
+ }
+
+ public boolean isRowAnimatingAway() {
+ return row != null && row.isHeadsUpAnimatingAway();
+ }
+
+ public boolean isRowHeadsUp() {
+ return row != null && row.isHeadsUp();
+ }
+
+ public void setHeadsUp(boolean shouldHeadsUp) {
+ if (row != null) row.setHeadsUp(shouldHeadsUp);
+ }
+
+ public boolean mustStayOnScreen() {
+ return row != null && row.mustStayOnScreen();
+ }
+
+ public void setHeadsUpIsVisible() {
+ if (row != null) row.setHeadsUpIsVisible();
+ }
+
+ //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+ public ExpandableNotificationRow getHeadsUpAnimationView() {
+ return row;
+ }
+
+ public void setUserLocked(boolean userLocked) {
+ if (row != null) row.setUserLocked(userLocked);
+ }
+
+ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+ if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+ }
+
+ public void setGroupExpansionChanging(boolean changing) {
+ if (row != null) row.setGroupExpansionChanging(changing);
+ }
+
+ public void notifyHeightChanged(boolean needsAnimation) {
+ if (row != null) row.notifyHeightChanged(needsAnimation);
+ }
+
+ public void closeRemoteInput() {
+ if (row != null) row.closeRemoteInput();
+ }
+
+ public boolean areChildrenExpanded() {
+ return row != null && row.areChildrenExpanded();
+ }
+
+ public boolean keepInParent() {
+ return row != null && row.keepInParent();
+ }
+
+ //TODO: probably less confusing to say "is group fully visible"
+ public boolean isGroupNotFullyVisible() {
+ return row == null || row.isGroupNotFullyVisible();
+ }
+
+ public NotificationGuts getGuts() {
+ if (row != null) return row.getGuts();
+ return null;
+ }
+
+ public boolean hasLowPriorityStateUpdated() {
+ return row != null && row.hasLowPriorityStateUpdated();
+ }
+
+ public void removeRow() {
+ if (row != null) row.setRemoved();
+ }
+
+ public boolean isSummaryWithChildren() {
+ return row != null && row.isSummaryWithChildren();
+ }
+
+ public void setKeepInParent(boolean keep) {
+ if (row != null) row.setKeepInParent(keep);
+ }
+
+ public void onDensityOrFontScaleChanged() {
+ if (row != null) row.onDensityOrFontScaleChanged();
+ }
+
+ public boolean areGutsExposed() {
+ return row != null && row.getGuts().isExposed();
+ }
+
+ public boolean isChildInGroup() {
+ return parent == null;
+ }
+
+ public void setLowPriorityStateUpdated(boolean updated) {
+ if (row != null) row.setLowPriorityStateUpdated(updated);
+ }
+
+ /**
+ * @return Can the underlying notification be cleared? This can be different from whether the
+ * notification can be dismissed in case notifications are sensitive on the lockscreen.
+ * @see #canViewBeDismissed()
+ */
+ public boolean isClearable() {
+ if (notification == null || !notification.isClearable()) {
+ return false;
+ }
+ if (children.size() > 0) {
+ for (int i = 0; i < children.size(); i++) {
+ Entry child = children.get(i);
+ if (!child.isClearable()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean canViewBeDismissed() {
+ if (row == null) return true;
+ return row.canViewBeDismissed();
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
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 0818513..16a3849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
@@ -108,13 +107,13 @@
VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final boolean ENABLE_HEADS_UP = true;
- protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
- protected final NotificationMessagingUtil mMessagingUtil;
+ private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
- protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+ private final NotificationClicker mNotificationClicker = new NotificationClicker();
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
@@ -144,15 +143,17 @@
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
private Callback mCallback;
+ private NotificationActivityStarter mNotificationActivityStarter;
protected PowerManager mPowerManager;
- protected NotificationListenerService.RankingMap mLatestRankingMap;
+ private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- protected ContentObserver mHeadsUpObserver;
+ private ContentObserver mHeadsUpObserver;
protected boolean mUseHeadsUp = false;
- protected boolean mDisableNotificationAlerts;
+ private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
- protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
+ @VisibleForTesting
+ final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@@ -176,9 +177,16 @@
}
// Check if the notification is displaying the menu, if so slide notification back
- if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
+ if (isMenuVisible(row)) {
row.animateTranslateNotification(0);
return;
+ } 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.
@@ -190,7 +198,11 @@
mBubbleController.collapseStack();
}
- mCallback.onNotificationClicked(sbn, row);
+ mNotificationActivityStarter.onNotificationClicked(sbn, row);
+ }
+
+ private boolean isMenuVisible(ExpandableNotificationRow row) {
+ return row.getProvider() != null && row.getProvider().isMenuVisible();
}
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
@@ -339,6 +351,11 @@
mOnAppOpsClickListener = mGutsManager::openGuts;
}
+ public void setNotificationActivityStarter(
+ NotificationActivityStarter notificationActivityStarter) {
+ mNotificationActivityStarter = notificationActivityStarter;
+ }
+
public NotificationData getNotificationData() {
return mNotificationData;
}
@@ -384,9 +401,9 @@
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
- if (entry.row != null) {
+ if (entry.rowExists()) {
entry.reset();
- updateNotification(entry, pmUser, sbn, entry.row);
+ updateNotification(entry, pmUser, sbn, entry.getRow());
} else {
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
@@ -485,22 +502,6 @@
updateNotifications();
}
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(StatusBarNotification n, String message) {
- removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
- try {
- mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
- n.getInitialPid(), message, n.getUserId());
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
private void abortExistingInflation(String key) {
if (mPendingNotifications.containsKey(key)) {
NotificationData.Entry entry = mPendingNotifications.get(key);
@@ -513,13 +514,31 @@
}
}
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
@Override
- public void handleInflationException(StatusBarNotification notification, Exception e) {
- handleNotificationError(notification, e.getMessage());
+ public void handleInflationException(StatusBarNotification n, Exception e) {
+ removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
+ try {
+ mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+ n.getInitialPid(), e.getMessage(), n.getUserId());
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
}
private void addEntry(NotificationData.Entry shadeEntry) {
- addNotificationViews(shadeEntry);
+ if (shadeEntry == null) {
+ return;
+ }
+ // Add the expanded view and icon.
+ mNotificationData.add(shadeEntry);
+ tagForeground(shadeEntry.notification);
+ updateNotifications();
mCallback.onNotificationAdded(shadeEntry);
}
@@ -540,14 +559,14 @@
// Mark as seen immediately
setNotificationShown(entry.notification);
} else {
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
if (shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
}
}
@@ -558,20 +577,20 @@
mPendingNotifications.remove(entry.key);
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
- if (!entry.row.isRemoved()) {
+ if (!entry.isRowRemoved()) {
boolean isNew = mNotificationData.get(entry.key) == null;
if (isNew) {
showAlertingView(entry, inflatedFlags);
addEntry(entry);
} else {
- if (entry.row.hasLowPriorityStateUpdated()) {
+ if (entry.getRow().hasLowPriorityStateUpdated()) {
mVisualStabilityManager.onLowPriorityUpdated(entry);
mPresenter.updateNotificationViews();
}
mGroupAlertTransferHelper.onInflationFinished(entry);
}
}
- entry.row.setLowPriorityStateUpdated(false);
+ entry.setLowPriorityStateUpdated(false);
}
@Override
@@ -630,9 +649,9 @@
getMediaManager().onNotificationRemoved(key);
mForegroundServiceController.removeNotification(entry.notification);
- if (entry.row != null) {
- entry.row.setRemoved();
- mListContainer.cleanUpViewState(entry.row);
+ if (entry.rowExists()) {
+ entry.removeRow();
+ mListContainer.cleanUpViewStateForEntry(entry);
}
// Let's remove the children if this was a summary
@@ -667,19 +686,19 @@
*/
private void handleGroupSummaryRemoved(String key) {
NotificationData.Entry entry = mNotificationData.get(key);
- if (entry != null && entry.row != null
- && entry.row.isSummaryWithChildren()) {
- if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+ if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
+ if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
// We don't want to remove children for autobundled notifications as they are not
// always cancelled. We only remove them if they were dismissed by the user.
return;
}
- List<ExpandableNotificationRow> notificationChildren =
- entry.row.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow row = notificationChildren.get(i);
- NotificationData.Entry childEntry = row.getEntry();
- boolean isForeground = (row.getStatusBarNotification().getNotification().flags
+ List<NotificationData.Entry> childEntries = entry.getChildren();
+ if (childEntries == null) {
+ return;
+ }
+ for (int i = 0; i < childEntries.size(); i++) {
+ NotificationData.Entry childEntry = childEntries.get(i);
+ boolean isForeground = (entry.notification.getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
boolean keepForReply =
getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -689,10 +708,10 @@
// a child we're keeping around for reply!
continue;
}
- row.setKeepInParent(true);
+ entry.setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
- row.setRemoved();
+ entry.removeRow();
}
}
}
@@ -702,15 +721,15 @@
mNotificationData.getNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
NotificationData.Entry entry = userNotifications.get(i);
- boolean exposedGuts = mGutsManager.getExposedGuts() != null
- && entry.row.getGuts() == mGutsManager.getExposedGuts();
- entry.row.onDensityOrFontScaleChanged();
+ entry.onDensityOrFontScaleChanged();
+ boolean exposedGuts = entry.areGutsExposed();
if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry.row);
+ mGutsManager.onDensityOrFontScaleChanged(entry);
}
}
}
+ //TODO: This method associates a row with an entry, but eventually needs to not do that
protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row) {
boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
@@ -733,8 +752,8 @@
entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
- entry.row = row;
- entry.row.setOnActivatedListener(mPresenter);
+ entry.setRow(row);
+ row.setOnActivatedListener(mPresenter);
boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
mNotificationData.getImportance(sbn.getKey()));
@@ -755,17 +774,7 @@
row.inflateViews();
}
- protected void addNotificationViews(NotificationData.Entry entry) {
- if (entry == null) {
- return;
- }
- // Add the expanded view and icon.
- mNotificationData.add(entry);
- tagForeground(entry.notification);
- updateNotifications();
- }
-
- protected NotificationData.Entry createNotificationViews(
+ private NotificationData.Entry createNotificationViews(
StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
throws InflationException {
if (DEBUG) {
@@ -773,7 +782,7 @@
}
NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
- if (shouldAutoBubble(entry)) {
+ if (BubbleController.shouldAutoBubble(getContext(), entry)) {
entry.setIsBubble(true);
}
@@ -841,7 +850,7 @@
}
@VisibleForTesting
- protected void tagForeground(StatusBarNotification notification) {
+ void tagForeground(StatusBarNotification notification) {
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
notification.getUserId(), notification.getPackageName());
if (activeOps != null) {
@@ -917,7 +926,7 @@
if (!notification.isClearable()) {
// The user may have performed a dismiss action on the notification, since it's
// not clearable we should snap it back.
- mListContainer.snapViewIfNeeded(entry.row);
+ mListContainer.snapViewIfNeeded(entry);
}
if (DEBUG) {
@@ -953,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.
@@ -970,15 +981,20 @@
if (NotificationUiAdjustment.needReinflate(
oldAdjustments.get(entry.key), newAdjustment)) {
- if (entry.row != null) {
+ if (entry.rowExists()) {
entry.reset();
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
- updateNotification(entry, pmUser, entry.notification, entry.row);
+ updateNotification(entry, pmUser, entry.notification, entry.getRow());
} else {
// 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();
+ }
}
}
@@ -1098,7 +1114,7 @@
* @param entry the entry to check
* @return true if the entry should ambient pulse, false otherwise
*/
- protected boolean shouldPulse(NotificationData.Entry entry) {
+ private boolean shouldPulse(NotificationData.Entry entry) {
StatusBarNotification sbn = entry.notification;
if (!getShadeController().isDozing()) {
@@ -1173,7 +1189,7 @@
return true;
}
- protected void setNotificationShown(StatusBarNotification n) {
+ private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1185,7 +1201,7 @@
}
}
- protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
@@ -1214,17 +1230,6 @@
}
}
-
- /**
- * Whether a bubble is appropriate to auto-bubble or not.
- */
- private boolean shouldAutoBubble(NotificationData.Entry entry) {
- int priority = mNotificationData.getImportance(entry.key);
- NotificationChannel channel = mNotificationData.getChannel(entry.key);
- boolean canAppOverlay = channel != null && channel.canOverlayApps();
- return BubbleController.shouldAutoBubble(entry, priority, canAppOverlay);
- }
-
/**
* Callback for NotificationEntryManager.
*/
@@ -1252,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/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 53ebe74..a194eef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,18 +16,16 @@
package com.android.systemui.statusbar.notification;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
/**
* An object that can determine the visibility of a Notification.
*/
public interface VisibilityLocationProvider {
/**
- * Returns whether an ExpandableNotificationRow is in a visible location or not.
+ * Returns whether an Entry is in a visible location or not.
*
- * @param row
+ * @param entry
* @return true if row is in a visible location
*/
- boolean isInVisibleLocation(ExpandableNotificationRow row);
+ boolean isInVisibleLocation(NotificationData.Entry entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 75613a4..fce7980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -120,7 +120,7 @@
return true;
}
if (mAllowedReorderViews.contains(row)
- && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
+ && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
return true;
}
return false;
@@ -142,12 +142,12 @@
if (isHeadsUp) {
// Heads up notifications should in general be allowed to reorder if they are out of
// view and stay at the current location if they aren't.
- mAllowedReorderViews.add(entry.row);
+ mAllowedReorderViews.add(entry.getRow());
}
}
public void onLowPriorityUpdated(NotificationData.Entry entry) {
- mLowPriorityReorderingViews.add(entry.row);
+ mLowPriorityReorderingViews.add(entry.getRow());
}
/**
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 5dfd5d0..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
@@ -115,7 +115,7 @@
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
- boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+ boolean isVisible = mListContainer.isInVisibleLocation(entry);
NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
if (isVisible) {
@@ -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 b6d99b2..8214ea6 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
@@ -17,22 +17,14 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
- .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
- .VISIBLE_TYPE_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
- .VISIBLE_TYPE_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
- .VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .InflationCallback;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -104,7 +96,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -336,7 +328,7 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private int mNotificationColorAmbient;
- private NotificationViewState mNotificationViewState;
+ private NotificationInlineImageResolver mImageResolver;
private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
new SystemNotificationAsyncTask();
@@ -597,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;
@@ -893,29 +892,32 @@
visualStabilityManager, callback);
}
- public void getChildrenStates(StackScrollState resultState,
- AmbientState ambientState) {
+ /** Updates states of all children. */
+ public void updateChildrenStates(AmbientState ambientState) {
if (mIsSummaryWithChildren) {
- ExpandableViewState parentState = resultState.getViewStateForView(this);
- mChildrenContainer.getState(resultState, parentState, ambientState);
+ ExpandableViewState parentState = getViewState();
+ mChildrenContainer.updateState(parentState, ambientState);
}
}
- public void applyChildrenState(StackScrollState state) {
+ /** Applies children states. */
+ public void applyChildrenState() {
if (mIsSummaryWithChildren) {
- mChildrenContainer.applyState(state);
+ mChildrenContainer.applyState();
}
}
- public void prepareExpansionChanged(StackScrollState state) {
+ /** Prepares expansion changed. */
+ public void prepareExpansionChanged() {
if (mIsSummaryWithChildren) {
- mChildrenContainer.prepareExpansionChanged(state);
+ mChildrenContainer.prepareExpansionChanged();
}
}
- public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
+ /** Starts child animations. */
+ public void startChildAnimation(AnimationProperties properties) {
if (mIsSummaryWithChildren) {
- mChildrenContainer.startAnimationToState(finalState, properties);
+ mChildrenContainer.startAnimationToState(properties);
}
}
@@ -1411,16 +1413,16 @@
public void performDismiss(boolean fromAccessibility) {
if (isOnlyChildInGroup()) {
- ExpandableNotificationRow groupSummary =
+ NotificationData.Entry groupSummary =
mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
if (groupSummary.isClearable()) {
// If this is the only child in the group, dismiss the group, but don't try to show
// the blocking helper affordance!
- groupSummary.performDismiss(fromAccessibility);
+ groupSummary.getRow().performDismiss(fromAccessibility);
}
}
setDismissed(fromAccessibility);
- if (isClearable()) {
+ if (mEntry.isClearable()) {
// TODO: track dismiss sentiment
if (mOnDismissRunnable != null) {
mOnDismissRunnable.run();
@@ -1620,6 +1622,8 @@
mFalsingManager = FalsingManager.getInstance(context);
mNotificationInflater = new NotificationInflater(this);
mMenuRow = new NotificationMenuRow(mContext);
+ mImageResolver = new NotificationInlineImageResolver(context,
+ new NotificationInlineImageCache());
initDimens();
}
@@ -1656,6 +1660,10 @@
res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
}
+ NotificationInlineImageResolver getImageResolver() {
+ return mImageResolver;
+ }
+
/**
* Resets this view so it can be re-used for an updated notification.
*/
@@ -2007,7 +2015,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) {
@@ -2017,8 +2026,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);
@@ -2046,7 +2059,7 @@
.setInterpolator(Interpolators.ALPHA_OUT);
setAboveShelf(true);
mExpandAnimationRunning = true;
- mNotificationViewState.cancelAnimations(this);
+ getViewState().cancelAnimations(this);
mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
} else {
mExpandAnimationRunning = false;
@@ -2073,6 +2086,8 @@
private void setChildIsExpanding(boolean isExpanding) {
mChildIsExpanding = isExpanding;
+ updateClipping();
+ invalidate();
}
@Override
@@ -2082,7 +2097,7 @@
@Override
protected boolean shouldClipToActualHeight() {
- return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding;
+ return super.shouldClipToActualHeight() && !mExpandAnimationRunning;
}
@Override
@@ -2244,28 +2259,6 @@
setRippleAllowed(allowed);
}
- /**
- * @return Can the underlying notification be cleared? This can be different from whether the
- * notification can be dismissed in case notifications are sensitive on the lockscreen.
- * @see #canViewBeDismissed()
- */
- public boolean isClearable() {
- if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
- return false;
- }
- if (mIsSummaryWithChildren) {
- List<ExpandableNotificationRow> notificationChildren =
- mChildrenContainer.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow child = notificationChildren.get(i);
- if (!child.isClearable()) {
- return false;
- }
- }
- }
- return true;
- }
-
@Override
public int getIntrinsicHeight() {
if (isShownAsBubble()) {
@@ -2533,10 +2526,10 @@
/**
* @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
* otherwise some state might not be updated. To request about the general clearability
- * see {@link #isClearable()}.
+ * see {@link NotificationData.Entry#isClearable()}.
*/
public boolean canViewBeDismissed() {
- return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
private boolean shouldShowPublic() {
@@ -2945,13 +2938,8 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
- mNotificationViewState = new NotificationViewState(stackScrollState);
- return mNotificationViewState;
- }
-
- public NotificationViewState getViewState() {
- return mNotificationViewState;
+ public ExpandableViewState createExpandableViewState() {
+ return new NotificationViewState();
}
@Override
@@ -3001,7 +2989,7 @@
return true;
}
} else if (child == mChildrenContainer) {
- if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
+ if (isClippingNeeded() || !hasNoRounding()) {
return true;
}
} else if (child instanceof NotificationGuts) {
@@ -3038,6 +3026,21 @@
return mOnAmbient;
}
+ //TODO: this logic can't depend on layout if we are recycling!
+ public boolean isMediaRow() {
+ return getExpandedContentView() != null
+ && getExpandedContentView().findViewById(
+ com.android.internal.R.id.media_actions) != null;
+ }
+
+ public boolean isTopLevelChild() {
+ return getParent() instanceof NotificationStackScrollLayout;
+ }
+
+ public boolean isGroupNotFullyVisible() {
+ return getClipTopAmount() > 0 || getTranslationY() < 0;
+ }
+
public void setAboveShelf(boolean aboveShelf) {
boolean wasAboveShelf = isAboveShelf();
mAboveShelf = aboveShelf;
@@ -3046,14 +3049,7 @@
}
}
- public static class NotificationViewState extends ExpandableViewState {
-
- private final StackScrollState mOverallState;
-
-
- private NotificationViewState(StackScrollState stackScrollState) {
- mOverallState = stackScrollState;
- }
+ private static class NotificationViewState extends ExpandableViewState {
@Override
public void applyToView(View view) {
@@ -3064,7 +3060,7 @@
}
handleFixedTranslationZ(row);
super.applyToView(view);
- row.applyChildrenState(mOverallState);
+ row.applyChildrenState();
}
}
@@ -3095,7 +3091,7 @@
}
handleFixedTranslationZ(row);
super.animateTo(child, properties);
- row.startChildAnimation(mOverallState, properties);
+ row.startChildAnimation(properties);
}
}
}
@@ -3145,17 +3141,25 @@
pw.print(", alpha: " + getAlpha());
pw.print(", translation: " + getTranslation());
pw.print(", removed: " + isRemoved());
- pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+ pw.print(", expandAnimationRunning: " + mExpandAnimationRunning);
+ NotificationContentView showingLayout = getShowingLayout();
+ pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
pw.println();
+ showingLayout.dump(fd, pw, args);
pw.print(" ");
- if (mNotificationViewState != null) {
- mNotificationViewState.dump(fd, pw, args);
+ if (getViewState() != null) {
+ getViewState().dump(fd, pw, args);
} else {
pw.print("no viewState!!!");
}
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 a7aed5f..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
@@ -115,12 +113,14 @@
if (!mCustomOutline) {
int translation = mShouldTranslateContents && !ignoreTranslation
? (int) getTranslation() : 0;
- left = Math.max(translation, 0);
+ int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+ left = Math.max(translation, 0) - halfExtraWidth;
top = mClipTopAmount + mBackgroundTop;
- right = getWidth() + Math.min(translation, 0);
+ right = getWidth() + halfExtraWidth + Math.min(translation, 0);
// If the top is rounded we want the bottom to be at most at the top roundness, in order
// to avoid the shadow changing when scrolling up.
- bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness));
+ bottom = Math.max(mMinimumHeightForClipping,
+ Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness)));
} else {
left = mOutlineRect.left;
top = mOutlineRect.top;
@@ -217,12 +217,16 @@
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();
}
@Override
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 0589e3f..20c4816 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,29 +21,35 @@
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;
+import androidx.annotation.Nullable;
+
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
/**
* An abstract view for expandable views.
*/
public abstract class ExpandableView extends FrameLayout implements Dumpable {
+ private static final String TAG = "ExpandableView";
public static final float NO_ROUNDNESS = -1;
protected OnHeightChangedListener mOnHeightChangedListener;
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();
@@ -54,6 +60,7 @@
private ViewGroup mTransientContainer;
private boolean mInShelf;
private boolean mTransformingInShelf;
+ @Nullable private ExpandableViewState mViewState;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -393,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;
}
@@ -511,10 +530,56 @@
public void setActualHeightAnimating(boolean animating) {}
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ protected ExpandableViewState createExpandableViewState() {
return new ExpandableViewState();
}
+ /** 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;
+ mViewState.alpha = 1f;
+ mViewState.notGoneIndex = -1;
+ mViewState.xTranslation = getTranslationX();
+ mViewState.hidden = false;
+ mViewState.scaleX = getScaleX();
+ mViewState.scaleY = getScaleY();
+ mViewState.inShelf = false;
+ mViewState.headsUpIsVisible = false;
+
+ // handling reset for child notifications
+ if (this instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) this;
+ List<ExpandableNotificationRow> children = row.getNotificationChildren();
+ if (row.isSummaryWithChildren() && children != null) {
+ for (ExpandableNotificationRow childRow : children) {
+ childRow.resetViewState();
+ }
+ }
+ }
+
+ return mViewState;
+ }
+
+ @Nullable public ExpandableViewState getViewState() {
+ return mViewState;
+ }
+
+ /** 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) {
+ mViewState.applyToView(this);
+ }
+ }
+
/**
* @return whether the current view doesn't add height to the overall content. This means that
* if it is added to a list of items, it's content will still have the same height.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 1f15ed0..311bf7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -24,7 +24,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
public class FooterView extends StackScrollerDecorView {
private final int mClearAllTopPadding;
@@ -87,7 +86,7 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
}
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 bb9a341..689d6d5 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
@@ -55,6 +55,8 @@
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyView;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -490,12 +492,12 @@
if (child == null) {
mExpandedChild = null;
mExpandedWrapper = null;
- if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- }
if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
mTransformationStartVisibleType = UNDEFINED;
}
+ if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
+ selectLayout(false /* animate */, true /* force */);
+ }
return;
}
addView(child);
@@ -528,12 +530,12 @@
if (child == null) {
mHeadsUpChild = null;
mHeadsUpWrapper = null;
- if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- }
if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
mTransformationStartVisibleType = UNDEFINED;
}
+ if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
+ selectLayout(false /* animate */, true /* force */);
+ }
return;
}
addView(child);
@@ -555,12 +557,12 @@
if (child == null) {
mAmbientChild = null;
mAmbientWrapper = null;
- if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- }
if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
mTransformationStartVisibleType = UNDEFINED;
}
+ if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
+ selectLayout(false /* animate */, true /* force */);
+ }
return;
}
addView(child);
@@ -1227,17 +1229,18 @@
mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
+ ExpandableNotificationRow row = entry.getRow();
if (mContractedChild != null) {
- mContractedWrapper.onContentUpdated(entry.row);
+ mContractedWrapper.onContentUpdated(row);
}
if (mExpandedChild != null) {
- mExpandedWrapper.onContentUpdated(entry.row);
+ mExpandedWrapper.onContentUpdated(row);
}
if (mHeadsUpChild != null) {
- mHeadsUpWrapper.onContentUpdated(entry.row);
+ mHeadsUpWrapper.onContentUpdated(row);
}
if (mAmbientChild != null) {
- mAmbientWrapper.onContentUpdated(entry.row);
+ mAmbientWrapper.onContentUpdated(row);
}
applyRemoteInputAndSmartReply(entry);
updateLegacy();
@@ -1287,10 +1290,10 @@
return;
}
- SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
- mSmartReplyConstants, entry);
+ SmartRepliesAndActions smartRepliesAndActions =
+ chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
- applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+ applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
applySmartReplyView(smartRepliesAndActions, entry);
}
@@ -1317,58 +1320,47 @@
boolean appGeneratedSmartRepliesExist =
enableAppGeneratedSmartReplies
&& remoteInputActionPair != null
- && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+ && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
+ && remoteInputActionPair.second.actionIntent != null;
List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+ SmartReplyView.SmartReplies smartReplies = null;
+ SmartReplyView.SmartActions smartActions = null;
if (appGeneratedSmartRepliesExist) {
- return new SmartRepliesAndActions(remoteInputActionPair.first,
- remoteInputActionPair.second.actionIntent,
+ smartReplies = new SmartReplyView.SmartReplies(
remoteInputActionPair.first.getChoices(),
- appGeneratedSmartActions,
- freeformRemoteInputActionPair);
- } else if (appGeneratedSmartActionsExist) {
- return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
- freeformRemoteInputActionPair);
- } else if (!ArrayUtils.isEmpty(entry.smartReplies)
- && freeformRemoteInputActionPair != null
- && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
- // App didn't generate anything, use NAS-generated replies and actions
- return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
- freeformRemoteInputActionPair.second.actionIntent,
- entry.smartReplies,
- entry.systemGeneratedSmartActions,
- freeformRemoteInputActionPair);
+ remoteInputActionPair.first,
+ remoteInputActionPair.second.actionIntent,
+ false /* fromAssistant */);
}
- // App didn't generate anything, and there are no NAS-generated smart replies.
- return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
- freeformRemoteInputActionPair);
- }
-
- @VisibleForTesting
- static class SmartRepliesAndActions {
- public final RemoteInput remoteInputWithChoices;
- public final PendingIntent pendingIntentForSmartReplies;
- public final CharSequence[] smartReplies;
- public final List<Notification.Action> smartActions;
- public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
-
- SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
- CharSequence[] choices, List<Notification.Action> smartActions,
- Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
- this.remoteInputWithChoices = remoteInput;
- this.pendingIntentForSmartReplies = pendingIntent;
- this.smartReplies = choices;
- this.smartActions = smartActions;
- this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
+ if (appGeneratedSmartActionsExist) {
+ smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
+ false /* fromAssistant */);
}
-
- boolean smartRepliesExist() {
- return remoteInputWithChoices != null
- && pendingIntentForSmartReplies != null
- && !ArrayUtils.isEmpty(smartReplies);
+ // Apps didn't provide any smart replies / actions, use those from NAS (if any).
+ if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
+ boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+ && freeformRemoteInputActionPair != null
+ && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
+ && freeformRemoteInputActionPair.second.actionIntent != null;
+ if (useGeneratedReplies) {
+ smartReplies = new SmartReplyView.SmartReplies(
+ entry.smartReplies,
+ freeformRemoteInputActionPair.first,
+ freeformRemoteInputActionPair.second.actionIntent,
+ true /* fromAssistant */);
+ }
+ boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
+ && notification.getAllowSystemGeneratedContextualActions();
+ if (useSmartActions) {
+ smartActions = new SmartReplyView.SmartActions(
+ entry.systemGeneratedSmartActions, true /* fromAssistant */);
+ }
}
+ return new SmartRepliesAndActions(
+ smartReplies, smartActions, freeformRemoteInputActionPair != null);
}
private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1475,12 +1467,9 @@
if (mExpandedChild != null) {
mExpandedSmartReplyView =
applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
- if (mExpandedSmartReplyView != null
- && smartRepliesAndActions.remoteInputWithChoices != null
- && smartRepliesAndActions.smartReplies != null
- && smartRepliesAndActions.smartReplies.length > 0) {
- mSmartReplyController.smartRepliesAdded(entry,
- smartRepliesAndActions.smartReplies.length);
+ if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
+ mSmartReplyController.smartRepliesAdded(
+ entry, smartRepliesAndActions.smartReplies.choices.length);
}
}
}
@@ -1494,8 +1483,8 @@
}
LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
// If there are no smart replies and no smart actions - early out.
- if (!smartRepliesAndActions.smartRepliesExist()
- && smartRepliesAndActions.smartActions.isEmpty()) {
+ if (smartRepliesAndActions.smartReplies == null
+ && smartRepliesAndActions.smartActions == null) {
smartReplyContainer.setVisibility(View.GONE);
return null;
}
@@ -1525,10 +1514,14 @@
}
if (smartReplyView != null) {
smartReplyView.resetSmartSuggestions(smartReplyContainer);
- smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
- smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
- entry, smartRepliesAndActions.smartReplies);
- smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+ if (smartRepliesAndActions.smartReplies != null) {
+ smartReplyView.addRepliesFromRemoteInput(
+ smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
+ }
+ if (smartRepliesAndActions.smartActions != null) {
+ smartReplyView.addSmartActions(
+ smartRepliesAndActions.smartActions, mSmartReplyController, entry);
+ }
smartReplyContainer.setVisibility(View.VISIBLE);
}
return smartReplyView;
@@ -1928,4 +1921,41 @@
mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
}
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" ");
+ pw.print("contentView visibility: " + getVisibility());
+ pw.print(", alpha: " + getAlpha());
+ pw.print(", clipBounds: " + getClipBounds());
+ pw.print(", contentHeight: " + mContentHeight);
+ pw.print(", visibleType: " + mVisibleType);
+ View view = getViewForVisibleType(mVisibleType);
+ pw.print(", visibleView ");
+ if (view != null) {
+ pw.print(" visibility: " + view.getVisibility());
+ pw.print(", alpha: " + view.getAlpha());
+ pw.print(", clipBounds: " + view.getClipBounds());
+ } else {
+ pw.print("null");
+ }
+ pw.println();
+ }
+
+ @VisibleForTesting
+ static class SmartRepliesAndActions {
+ @Nullable
+ public final SmartReplyView.SmartReplies smartReplies;
+ @Nullable
+ public final SmartReplyView.SmartActions smartActions;
+ public final boolean hasFreeformRemoteInput;
+
+ SmartRepliesAndActions(
+ @Nullable SmartReplyView.SmartReplies smartReplies,
+ @Nullable SmartReplyView.SmartActions smartActions,
+ boolean hasFreeformRemoteInput) {
+ this.smartReplies = smartReplies;
+ this.smartActions = smartActions;
+ this.hasFreeformRemoteInput = hasFreeformRemoteInput;
+ }
+ }
}
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 37bf06e..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
@@ -18,17 +18,13 @@
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import android.app.INotificationManager;
import android.app.NotificationChannel;
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;
@@ -43,16 +39,15 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
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;
@@ -83,14 +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 final IStatusBarService mBarService;
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;
@@ -99,12 +93,8 @@
public NotificationGutsManager(Context context) {
mContext = context;
- Resources res = context.getResources();
-
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -116,9 +106,14 @@
mOnSettingsClickListener = onSettingsClick;
}
- public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
- setExposedGuts(row.getGuts());
- bindGuts(row);
+ public void setNotificationActivityStarter(
+ NotificationActivityStarter notificationActivityStarter) {
+ mNotificationActivityStarter = notificationActivityStarter;
+ }
+
+ public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+ setExposedGuts(entry.getGuts());
+ bindGuts(entry.getRow());
}
/**
@@ -134,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,
@@ -145,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);
}
}
@@ -191,13 +186,7 @@
} else if (gutsView instanceof AppOpsInfo) {
initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
} else if (gutsView instanceof NotificationInfo) {
- int action;
- if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
- action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
- } else {
- action = ACTION_NONE;
- }
- initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
+ initializeNotificationInfo(row, (NotificationInfo) gutsView);
}
return true;
} catch (Exception e) {
@@ -256,13 +245,11 @@
* Sets up the {@link NotificationInfo} inside the notification row's guts.
* @param row view to set up the guts for
* @param notificationInfoView view to set up/bind within {@code row}
- * @param action The action to take immediately upon binding, if any.
*/
@VisibleForTesting
void initializeNotificationInfo(
final ExpandableNotificationRow row,
- NotificationInfo notificationInfoView,
- @NotificationInfo.NotificationInfoAction int action) throws Exception {
+ NotificationInfo notificationInfoView) throws Exception {
NotificationGuts guts = row.getGuts();
StatusBarNotification sbn = row.getStatusBarNotification();
String packageName = sbn.getPackageName();
@@ -277,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();
@@ -305,9 +293,7 @@
row.getIsNonblockable(),
isForBlockingHelper,
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
- row.getEntry().noisy,
- row.getEntry().importance,
- action);
+ row.getEntry().importance);
}
@@ -441,8 +427,8 @@
public boolean shouldExtendLifetime(NotificationData.Entry entry) {
return entry != null
&&(mNotificationGutsExposed != null
- && entry.row.getGuts() != null
- && mNotificationGutsExposed == entry.row.getGuts()
+ && entry.getGuts() != null
+ && mNotificationGutsExposed == entry.getGuts()
&& !mNotificationGutsExposed.isLeavebehind());
}
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/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 3a7091b..5329541 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -92,6 +92,7 @@
private String mPackageName;
private String mAppName;
private int mAppUid;
+ private String mDelegatePkg;
private int mNumUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
@@ -103,7 +104,6 @@
private AnimatorSet mExpandAnimation;
private boolean mIsForeground;
private boolean mIsDeviceProvisioned;
- private boolean mIsNoisy;
private CheckSaveListener mCheckSaveListener;
private OnSettingsClickListener mOnSettingsClickListener;
@@ -185,15 +185,13 @@
final OnAppSettingsClickListener onAppSettingsClick,
boolean isDeviceProvisioned,
boolean isNonblockable,
- boolean isNoisy,
- int importance,
- @NotificationInfoAction int action)
+ int importance)
throws RemoteException {
bindNotification(pm, iNotificationManager, pkg, notificationChannel,
numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
- false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
- importance, action);
+ false /* isBlockingHelper */, false /* isUserSentimentNegative */,
+ importance);
}
public void bindNotification(
@@ -210,9 +208,7 @@
boolean isNonblockable,
boolean isForBlockingHelper,
boolean isUserSentimentNegative,
- boolean isNoisy,
- int importance,
- @NotificationInfoAction int action)
+ int importance)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -235,8 +231,8 @@
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
mIsForBlockingHelper = isForBlockingHelper;
mAppUid = mSbn.getUid();
+ mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
- mIsNoisy = isNoisy;
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
@@ -253,10 +249,6 @@
bindHeader();
bindPrompt();
bindButtons();
-
- if (action != ACTION_NONE) {
- swapContent(action, false /* don't animate */);
- }
}
private void bindHeader() throws RemoteException {
@@ -281,26 +273,8 @@
((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
- // Set group information if this channel has an associated group.
- CharSequence groupName = null;
- if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
- final NotificationChannelGroup notificationChannelGroup =
- mINotificationManager.getNotificationChannelGroupForPackage(
- mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
- if (notificationChannelGroup != null) {
- groupName = notificationChannelGroup.getName();
- }
- }
- TextView groupNameView = findViewById(R.id.group_name);
- TextView groupDividerView = findViewById(R.id.pkg_group_divider);
- if (groupName != null) {
- groupNameView.setText(groupName);
- groupNameView.setVisibility(View.VISIBLE);
- groupDividerView.setVisibility(View.VISIBLE);
- } else {
- groupNameView.setVisibility(View.GONE);
- groupDividerView.setVisibility(View.GONE);
- }
+ // Delegate
+ bindDelegate();
// Settings button.
final View settingsButton = findViewById(R.id.info);
@@ -320,9 +294,10 @@
}
}
- private void bindPrompt() {
+ private void bindPrompt() throws RemoteException {
final TextView blockPrompt = findViewById(R.id.block_prompt);
bindName();
+ bindGroup();
if (mIsNonblockable) {
blockPrompt.setText(R.string.notification_unblockable_desc);
} else {
@@ -345,6 +320,60 @@
}
}
+ private void bindDelegate() {
+ TextView delegateView = findViewById(R.id.delegate_name);
+ TextView dividerView = findViewById(R.id.pkg_divider);
+
+ CharSequence delegatePkg = null;
+ if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+ // this notification was posted by a delegate!
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ mDelegatePkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) { }
+ }
+ if (delegatePkg != null) {
+ delegateView.setText(mContext.getResources().getString(
+ R.string.notification_delegate_header, delegatePkg));
+ delegateView.setVisibility(View.VISIBLE);
+ dividerView.setVisibility(View.VISIBLE);
+ } else {
+ delegateView.setVisibility(View.GONE);
+ dividerView.setVisibility(View.GONE);
+ }
+ }
+
+ private void bindGroup() throws RemoteException {
+ // Set group information if this channel has an associated group.
+ CharSequence groupName = null;
+ if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+ final NotificationChannelGroup notificationChannelGroup =
+ mINotificationManager.getNotificationChannelGroupForPackage(
+ mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+ if (notificationChannelGroup != null) {
+ groupName = notificationChannelGroup.getName();
+ }
+ }
+ TextView groupNameView = findViewById(R.id.group_name);
+ TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+ if (groupName != null) {
+ groupNameView.setText(groupName);
+ groupNameView.setVisibility(View.VISIBLE);
+ groupDividerView.setVisibility(View.VISIBLE);
+ } else {
+ groupNameView.setVisibility(View.GONE);
+ groupDividerView.setVisibility(View.GONE);
+ }
+ }
+
@VisibleForTesting
void logBlockingHelperCounter(String counterTag) {
if (mIsForBlockingHelper) {
@@ -378,54 +407,74 @@
}
private void bindButtons() {
- // Set up stay-in-notification actions
- View block = findViewById(R.id.block);
- TextView keep = findViewById(R.id.keep);
- TextView silent = findViewById(R.id.toggle_silent);
- View minimize = findViewById(R.id.minimize);
-
findViewById(R.id.undo).setOnClickListener(mOnUndo);
- block.setOnClickListener(mOnStopOrMinimizeNotifications);
- keep.setOnClickListener(mOnKeepShowing);
- silent.setOnClickListener(mOnToggleSilent);
- minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
- if (mIsNonblockable) {
- keep.setText(android.R.string.ok);
- block.setVisibility(GONE);
- silent.setVisibility(GONE);
- minimize.setVisibility(GONE);
- } else if (mIsForeground) {
- block.setVisibility(GONE);
- silent.setVisibility(GONE);
- minimize.setVisibility(VISIBLE);
- } else {
- block.setVisibility(VISIBLE);
- boolean showToggleSilent = mIsNoisy
- && NotificationUtils.useNewInterruptionModel(mContext);
- silent.setVisibility(showToggleSilent ? VISIBLE : GONE);
+ boolean showInterruptivenessSettings =
+ !mIsNonblockable
+ && !mIsForeground
+ && !mIsForBlockingHelper
+ && NotificationUtils.useNewInterruptionModel(mContext);
+ if (showInterruptivenessSettings) {
+ findViewById(R.id.block_or_minimize).setVisibility(GONE);
+ findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
+ View block = findViewById(R.id.int_block);
+ TextView silent = findViewById(R.id.int_silent);
+ TextView alert = findViewById(R.id.int_alert);
+
boolean isCurrentlyAlerting =
mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT;
- silent.setText(isCurrentlyAlerting
- ? R.string.inline_silent_button_silent
- : R.string.inline_silent_button_alert);
- minimize.setVisibility(GONE);
- }
- // Set up app settings link (i.e. Customize)
- TextView settingsLinkView = findViewById(R.id.app_settings);
- Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
- mSbn.getId(), mSbn.getTag());
- if (!mIsForBlockingHelper
- && settingsIntent != null
- && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
- settingsLinkView.setVisibility(VISIBLE);
- settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
- settingsLinkView.setOnClickListener((View view) -> {
- mAppSettingsClickListener.onClick(view, settingsIntent);
- });
+ block.setOnClickListener(mOnStopOrMinimizeNotifications);
+ if (isCurrentlyAlerting) {
+ silent.setOnClickListener(mOnToggleSilent);
+ silent.setText(R.string.inline_silent_button_silent);
+ alert.setOnClickListener(mOnKeepShowing);
+ alert.setText(R.string.inline_silent_button_keep_alerting);
+ } else {
+ silent.setOnClickListener(mOnKeepShowing);
+ silent.setText(R.string.inline_silent_button_stay_silent);
+ alert.setOnClickListener(mOnToggleSilent);
+ alert.setText(R.string.inline_silent_button_alert);
+ }
} else {
- settingsLinkView.setVisibility(View.GONE);
+ findViewById(R.id.block_or_minimize).setVisibility(VISIBLE);
+ findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
+ View block = findViewById(R.id.block);
+ TextView keep = findViewById(R.id.keep);
+ View minimize = findViewById(R.id.minimize);
+
+ block.setOnClickListener(mOnStopOrMinimizeNotifications);
+ keep.setOnClickListener(mOnKeepShowing);
+ minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
+
+ if (mIsNonblockable) {
+ keep.setText(android.R.string.ok);
+ block.setVisibility(GONE);
+ minimize.setVisibility(GONE);
+ } else if (mIsForeground) {
+ block.setVisibility(GONE);
+ minimize.setVisibility(VISIBLE);
+ } else {
+ block.setVisibility(VISIBLE);
+ minimize.setVisibility(GONE);
+ }
+
+ // Set up app settings link (i.e. Customize)
+ TextView settingsLinkView = findViewById(R.id.app_settings);
+ Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
+ mSingleNotificationChannel,
+ mSbn.getId(), mSbn.getTag());
+ if (!mIsForBlockingHelper
+ && settingsIntent != null
+ && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+ settingsLinkView.setVisibility(VISIBLE);
+ settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
+ settingsLinkView.setOnClickListener((View view) -> {
+ mAppSettingsClickListener.onClick(view, settingsIntent);
+ });
+ } else {
+ settingsLinkView.setVisibility(View.GONE);
+ }
}
}
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 b6ff6fc..50564e3 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
@@ -17,9 +17,6 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -46,7 +43,6 @@
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
@@ -73,7 +69,7 @@
private Context mContext;
private FrameLayout mMenuContainer;
- private NotificationInfoMenuItem mInfoItem;
+ private NotificationMenuItem mInfoItem;
private MenuItem mAppOpsItem;
private MenuItem mSnoozeItem;
private ArrayList<MenuItem> mLeftMenuItems;
@@ -248,36 +244,32 @@
if (!isForeground) {
// Only show snooze for non-foreground notifications
mSnoozeItem = createSnoozeItem(mContext);
- mLeftMenuItems.add(mSnoozeItem);
}
- mInfoItem = createInfoItem(mContext);
- if (!NotificationUtils.useNewInterruptionModel(mContext)) {
- mLeftMenuItems.add(mInfoItem);
- }
-
mAppOpsItem = createAppOpsItem(mContext);
- mLeftMenuItems.add(mAppOpsItem);
-
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- if (!mParent.getIsNonblockable()) {
- mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView()));
- }
- // TODO(kprevas): this is duplicated logic
- // but it's currently spread across NotificationGutsManager and NotificationInfo.
- // Try to consolidate and reuse here.
- boolean canToggleSilent = !mParent.getIsNonblockable()
- && !isForeground
- && mParent.getEntry().noisy;
- if (canToggleSilent) {
- int channelImportance = mParent.getEntry().channel.getImportance();
- int effectiveImportance =
- channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
- ? mParent.getEntry().importance : channelImportance;
- mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(),
- effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT));
- }
+ int channelImportance = mParent.getEntry().channel.getImportance();
+ int effectiveImportance =
+ channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
+ ? mParent.getEntry().importance : channelImportance;
+ mInfoItem = createInfoItem(mContext,
+ effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT);
} else {
- mRightMenuItems.addAll(mLeftMenuItems);
+ mInfoItem = createInfoItem(mContext);
+ }
+
+ if (!NotificationUtils.useNewInterruptionModel(mContext)) {
+ if (!isForeground) {
+ mRightMenuItems.add(mSnoozeItem);
+ }
+ mRightMenuItems.add(mInfoItem);
+ mRightMenuItems.add(mAppOpsItem);
+ mLeftMenuItems.addAll(mRightMenuItems);
+ } else {
+ mRightMenuItems.add(mInfoItem);
+ mRightMenuItems.add(mAppOpsItem);
+ if (!isForeground) {
+ mRightMenuItems.add(mSnoozeItem);
+ }
}
populateMenuViews();
@@ -634,13 +626,24 @@
return snooze;
}
- static NotificationInfoMenuItem createInfoItem(Context context) {
+ static NotificationMenuItem createInfoItem(Context context) {
Resources res = context.getResources();
String infoDescription = res.getString(R.string.notification_menu_gear_description);
NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
R.layout.notification_info, null, false);
- return new NotificationInfoMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings, ACTION_NONE);
+ return new NotificationMenuItem(context, infoDescription, infoContent,
+ R.drawable.ic_settings);
+ }
+
+ static NotificationMenuItem createInfoItem(Context context, boolean isCurrentlySilent) {
+ Resources res = context.getResources();
+ String infoDescription = res.getString(R.string.notification_menu_gear_description);
+ 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;
+ return new NotificationMenuItem(context, infoDescription, infoContent, iconResId);
}
static MenuItem createAppOpsItem(Context context) {
@@ -651,29 +654,6 @@
return info;
}
- private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) {
- return new NotificationInfoMenuItem(
- context,
- context.getResources().getString(R.string.inline_stop_button),
- gutsView,
- R.drawable.ic_notification_block,
- ACTION_BLOCK);
- }
-
- private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView,
- boolean isCurrentlySilent) {
- return new NotificationInfoMenuItem(
- context,
- isCurrentlySilent
- ? context.getResources().getString(R.string.inline_silent_button_alert)
- : context.getResources().getString(R.string.inline_silent_button_silent),
- gutsView,
- isCurrentlySilent
- ? R.drawable.ic_notifications_alert
- : R.drawable.ic_notifications_silence,
- ACTION_TOGGLE_SILENT);
- }
-
private void addMenuView(MenuItem item, ViewGroup parent) {
View menuView = item.getMenuView();
if (menuView != null) {
@@ -789,23 +769,4 @@
return mContentDescription;
}
}
-
- /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
- public static class NotificationInfoMenuItem extends NotificationMenuItem {
-
- @NotificationInfoAction
- int mAction;
-
- public NotificationInfoMenuItem(Context context, String contentDescription,
- NotificationInfo content, int iconResId,
- @NotificationInfoAction int action) {
- super(context, contentDescription, content, iconResId);
- this.mAction = action;
- }
-
- @Override
- public NotificationInfo getGutsView() {
- return (NotificationInfo) super.getGutsView();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ff1a6fc..670908f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -40,7 +40,7 @@
private static final int NO_SECTION_BOUNDARY = -1;
- private ArrayList<View> mDraggedViews = new ArrayList<>();
+ private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>();
private int mScrollY;
private boolean mDimmed;
private ActivatableNotificationView mActivatedChild;
@@ -131,7 +131,8 @@
this.mScrollY = scrollY;
}
- public void onBeginDrag(View view) {
+ /** Call when dragging begins. */
+ public void onBeginDrag(ExpandableView view) {
mDraggedViews.add(view);
}
@@ -139,7 +140,7 @@
mDraggedViews.remove(view);
}
- public ArrayList<View> getDraggedViews() {
+ public ArrayList<ExpandableView> getDraggedViews() {
return mDraggedViews;
}
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 74b4aa2..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
@@ -274,7 +274,7 @@
updateGroupOverflow();
row.setContentTransformationAmount(0, false /* isLastChild */);
// It doesn't make sense to keep old animations around, lets cancel them!
- ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
+ ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
viewState.cancelAnimations(row);
row.cancelAppearDrawing();
@@ -562,12 +562,10 @@
/**
* Update the state of all its children based on a linear layout algorithm.
- * @param resultState the state to update
* @param parentState the state of the parent
* @param ambientState
*/
- public void getState(StackScrollState resultState, ExpandableViewState parentState,
- AmbientState ambientState) {
+ public void updateState(ExpandableViewState parentState, AmbientState ambientState) {
int childCount = mChildren.size();
int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
boolean firstChild = true;
@@ -605,7 +603,7 @@
firstChild = false;
}
- ExpandableViewState childState = resultState.getViewStateForView(child);
+ ExpandableViewState childState = child.getViewState();
int intrinsicHeight = child.getIntrinsicHeight();
childState.height = intrinsicHeight;
childState.yTranslation = yPosition + launchTransitionCompensation;
@@ -639,7 +637,7 @@
if (mOverflowNumber != null) {
ExpandableNotificationRow overflowView = mChildren.get(Math.min(
getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
- mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
+ mGroupOverFlowState.copyFrom(overflowView.getViewState());
if (mContainingNotification.isOnAmbient()) {
mGroupOverFlowState.alpha = 0.0f;
@@ -724,7 +722,8 @@
return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
}
- public void applyState(StackScrollState state) {
+ /** Applies state to children. */
+ public void applyState() {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
float expandFraction = 0.0f;
@@ -737,7 +736,7 @@
&& !mHideDividersDuringExpand);
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
- ExpandableViewState viewState = state.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
viewState.applyToView(child);
// layout the divider
@@ -799,14 +798,14 @@
* This is called when the children expansion has changed and positions the children properly
* for an appear animation.
*
- * @param state the new state we animate to
*/
- public void prepareExpansionChanged(StackScrollState state) {
+ public void prepareExpansionChanged() {
// TODO: do something that makes sense, like placing the invisible views correctly
return;
}
- public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
+ /** Animate to a given state. */
+ public void startAnimationToState(AnimationProperties properties) {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
float expandFraction = getGroupExpandFraction();
@@ -816,7 +815,7 @@
&& !mHideDividersDuringExpand);
for (int i = childCount - 1; i >= 0; i--) {
ExpandableNotificationRow child = mChildren.get(i);
- ExpandableViewState viewState = state.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
viewState.animateTo(child, properties);
// layout the divider
@@ -876,6 +875,7 @@
ExpandableNotificationRow child = mChildren.get(childIdx);
child.setChildrenExpanded(childrenExpanded, false);
}
+ updateHeaderTouchability();
}
public void setContainingNotification(ExpandableNotificationRow parent) {
@@ -1216,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/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 4d100a4..f0a2653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -45,34 +45,31 @@
/**
* Change the position of child to a new location
- *
- * @param child the view to change the position for
+ * @param child the view to change the position for
* @param newIndex the new index
*/
- void changeViewPosition(View child, int newIndex);
+ void changeViewPosition(ExpandableView child, int newIndex);
/**
* Called when a child was added to a group.
*
* @param row row of the group child that was added
*/
- void notifyGroupChildAdded(View row);
+ void notifyGroupChildAdded(ExpandableView row);
/**
* Called when a child was removed from a group.
- *
- * @param row row of the child that was removed
+ * @param row row of the child that was removed
* @param childrenContainer ViewGroup of the group that the child was removed from
*/
- void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
+ void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer);
/**
* Generate an animation for an added child view.
- *
- * @param child The view to be added.
+ * @param child The view to be added.
* @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
*/
- void generateAddAnimation(View child, boolean fromMoreCard);
+ void generateAddAnimation(ExpandableView child, boolean fromMoreCard);
/**
* Generate a child order changed event.
@@ -118,9 +115,9 @@
/**
* Handle snapping a non-dismissable row back if the user tried to dismiss it.
*
- * @param row row to snap back
+ * @param entry the entry whose row needs to snap back
*/
- void snapViewIfNeeded(ExpandableNotificationRow row);
+ void snapViewIfNeeded(NotificationData.Entry entry);
/**
* Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
@@ -149,9 +146,9 @@
* Called when a notification is removed from the shade. This cleans up the state for a
* given view.
*
- * @param view view to clean up view state for
+ * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
*/
- void cleanUpViewState(View view);
+ void cleanUpViewStateForEntry(NotificationData.Entry entry);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index c867a41..4f0831f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,13 +16,12 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
- .NUM_SECTIONS;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
-import android.view.View;
-
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
@@ -37,7 +36,7 @@
private ActivatableNotificationView[] mLastInSectionViews;
private ActivatableNotificationView[] mTmpFirstInSectionViews;
private ActivatableNotificationView[] mTmpLastInSectionViews;
- private HashSet<View> mAnimatedChildren;
+ private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
@@ -50,13 +49,13 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- updateView(headsUp, false /* animate */);
+ public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+ updateView(headsUp.getRow(), false /* animate */);
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
- updateView(headsUp, true /* animate */);
+ public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+ updateView(headsUp.getRow(), true /* animate */);
}
public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
@@ -211,7 +210,7 @@
return anyChanged;
}
- public void setAnimatedChildren(HashSet<View> animatedChildren) {
+ public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) {
mAnimatedChildren = animatedChildren;
}
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 ecd0d98..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
@@ -18,6 +18,8 @@
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
+ .ANIMATION_DURATION_SWIPE;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
import android.animation.Animator;
@@ -208,16 +210,12 @@
*/
protected final StackScrollAlgorithm mStackScrollAlgorithm;
- /**
- * The current State this Layout is in
- */
- private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
private final AmbientState mAmbientState;
private NotificationGroupManager mGroupManager;
- private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
+ private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
- private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
- private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
+ private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
+ private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
private ArrayList<View> mSwipedOutViews = new ArrayList<>();
@@ -275,7 +273,7 @@
private boolean mDontReportNextOverScroll;
private boolean mDontClampNextScroll;
private boolean mNeedViewResizeAnimation;
- private View mExpandedGroupView;
+ private ExpandableView mExpandedGroupView;
private boolean mEverythingNeedsAnimation;
/**
@@ -382,7 +380,7 @@
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
- private View mNeedingPulseAnimation;
+ private ExpandableView mNeedingPulseAnimation;
/**
* @see #setDarkAmount(float, float)
@@ -600,12 +598,12 @@
public void setRemoteInputActive(NotificationData.Entry entry,
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
- entry.row.notifyHeightChanged(true /* needsAnimation */);
+ entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
}
public void lockScrollTo(NotificationData.Entry entry) {
- NotificationStackScrollLayout.this.lockScrollTo(entry.row);
+ NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
}
public void requestDisallowLongPressAndDismiss() {
@@ -620,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);
}
@@ -628,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);
}
@@ -897,8 +895,10 @@
@Override
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- public boolean isInVisibleLocation(ExpandableNotificationRow row) {
- ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
+ public boolean isInVisibleLocation(NotificationData.Entry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ ExpandableViewState childViewState = row.getViewState();
+
if (childViewState == null) {
return false;
}
@@ -944,7 +944,7 @@
? 0
: mScroller.getCurrVelocity());
mAmbientState.setScrollY(mOwnScrollY);
- mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
+ mStackScrollAlgorithm.resetViewStates(mAmbientState);
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
@@ -1213,12 +1213,12 @@
if (topEntry == null) {
return 0;
}
- ExpandableNotificationRow row = topEntry.row;
+ ExpandableNotificationRow row = topEntry.getRow();
if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
+ final NotificationData.Entry groupSummary
= mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null) {
- row = groupSummary;
+ row = groupSummary.getRow();
}
}
return row.getPinnedHeadsUpHeight();
@@ -1390,11 +1390,12 @@
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+ NotificationData.Entry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().row != row
+ && mHeadsUpManager.getTopEntry().getRow() != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
- != row) {
+ mHeadsUpManager.getTopEntry().notification)
+ != entry) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
@@ -1524,7 +1525,8 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void snapViewIfNeeded(ExpandableNotificationRow child) {
+ public void snapViewIfNeeded(NotificationData.Entry entry) {
+ ExpandableNotificationRow child = entry.getRow();
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
@@ -1934,12 +1936,12 @@
* @return the last child which has visibility unequal to GONE
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public View getLastChildNotGone() {
+ public ExpandableView getLastChildNotGone() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child != mShelf) {
- return child;
+ return (ExpandableView) child;
}
}
return null;
@@ -2508,35 +2510,33 @@
// we only call our internal methods if this is actually a removal and not just a
// notification which becomes a child notification
if (!mChildTransferInProgress) {
- onViewRemovedInternal(child, this);
+ onViewRemovedInternal((ExpandableView) child, this);
}
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@Override
- public void cleanUpViewState(View child) {
+ public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+ View child = entry.getRow();
if (child == mSwipeHelper.getTranslatingParentView()) {
mSwipeHelper.clearTranslatingParentView();
}
- mCurrentStackScrollState.removeViewStateForView(child);
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void onViewRemovedInternal(View child, ViewGroup container) {
+ private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
return;
}
- ExpandableView expandableView = (ExpandableView) child;
- expandableView.setOnHeightChangedListener(null);
- mCurrentStackScrollState.removeViewStateForView(child);
- updateScrollStateForRemovedChild(expandableView);
+ child.setOnHeightChangedListener(null);
+ updateScrollStateForRemovedChild(child);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) {
if (!mSwipedOutViews.contains(child)
- || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+ || Math.abs(child.getTranslation()) != child.getWidth()) {
container.addTransientView(child, 0);
- expandableView.setTransientContainer(container);
+ child.setTransientContainer(container);
}
} else {
mSwipedOutViews.remove(child);
@@ -2580,14 +2580,14 @@
* @return Whether an animation was generated.
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private boolean generateRemoveAnimation(View child) {
+ private boolean generateRemoveAnimation(ExpandableView child) {
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
mAddedHeadsUpChildren.remove(child);
return false;
}
if (isClickedHeadsUp(child)) {
// An animation is already running, add it transiently
- mClearTransientViewsWhenFinished.add((ExpandableView) child);
+ mClearTransientViewsWhenFinished.add(child);
return true;
}
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
@@ -2644,9 +2644,9 @@
private boolean isChildInInvisibleGroup(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- ExpandableNotificationRow groupSummary =
+ NotificationData.Entry groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null && groupSummary != row) {
+ if (groupSummary != null && groupSummary.getRow() != row) {
return row.getVisibility() == View.INVISIBLE;
}
}
@@ -2761,7 +2761,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onViewAdded(View child) {
super.onViewAdded(child);
- onViewAddedInternal(child);
+ onViewAddedInternal((ExpandableView) child);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2831,31 +2831,28 @@
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void onViewAddedInternal(View child) {
+ private void onViewAddedInternal(ExpandableView child) {
updateHideSensitiveForChild(child);
- ((ExpandableView) child).setOnHeightChangedListener(this);
+ child.setOnHeightChangedListener(this);
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
updateChronometerForChild(child);
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void updateHideSensitiveForChild(View child) {
- if (child instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) child;
- expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
- }
+ private void updateHideSensitiveForChild(ExpandableView child) {
+ child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
}
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
+ public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
onViewRemovedInternal(row, childrenContainer);
}
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void notifyGroupChildAdded(View row) {
+ public void notifyGroupChildAdded(ExpandableView row) {
onViewAddedInternal(row);
}
@@ -2927,7 +2924,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void generateAddAnimation(View child, boolean fromMoreCard) {
+ public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
// Generate Animations
mChildrenToAddAnimated.add(child);
@@ -2944,7 +2941,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void changeViewPosition(View child, int newIndex) {
+ public void changeViewPosition(ExpandableView child, int newIndex) {
int currentIndex = indexOfChild(child);
if (currentIndex == -1) {
@@ -2983,8 +2980,7 @@
}
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
setAnimationRunning(true);
- mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
- mGoToFullShadeDelay);
+ mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
@@ -3031,7 +3027,7 @@
continue;
}
} else {
- ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+ ExpandableViewState viewState = row.getViewState();
if (viewState == null) {
// A view state was never generated for this view, so we don't need to animate
// this. This may happen with notification children.
@@ -3097,7 +3093,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateChildRemovalEvents() {
- for (View child : mChildrenToRemoveAnimated) {
+ for (ExpandableView child : mChildrenToRemoveAnimated) {
boolean childWasSwipedOut = mSwipedOutViews.contains(child);
// we need to know the view after this one
@@ -3139,7 +3135,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generatePositionChangeEvents() {
- for (View child : mChildrenChangingPositions) {
+ for (ExpandableView child : mChildrenChangingPositions) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
}
@@ -3153,7 +3149,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateChildAdditionEvents() {
- for (View child : mChildrenToAddAnimated) {
+ for (ExpandableView child : mChildrenToAddAnimated) {
if (mFromMoreCardAdditions.contains(child)) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_ADD,
@@ -3245,7 +3241,7 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context);
+ return new StackScrollAlgorithm(context, this);
}
/**
@@ -3910,7 +3906,7 @@
mStatusBar.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
- ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+ ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
if (draggedViews.size() > 0) {
draggedViews.clear();
updateContinuousShadowDrawing();
@@ -4194,7 +4190,12 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void applyCurrentState() {
- mCurrentStackScrollState.apply();
+ int numChildren = getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ child.applyViewState();
+ }
+
if (mListener != null) {
mListener.onChildLocationsChanged();
}
@@ -4662,6 +4663,11 @@
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
}
+ public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+ ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+ generateHeadsUpAnimation(row, isHeadsUp);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
@@ -4933,8 +4939,7 @@
if (!(child instanceof ExpandableNotificationRow)) {
pw.println(" " + child.getClass().getSimpleName());
// Notifications dump it's viewstate as part of their dump to support children
- ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(
- child);
+ ExpandableViewState viewState = child.getViewState();
if (viewState == null) {
pw.println(" no viewState!!!");
} else {
@@ -4951,7 +4956,7 @@
ExpandableView child = (ExpandableView) getTransientView(i);
child.dump(fd, pw, args);
}
- ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+ ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
int draggedCount = draggedViews.size();
pw.println(" Dragged Views: " + draggedCount);
for (int i = 0; i < draggedCount; i++) {
@@ -5090,7 +5095,7 @@
if (i == 0) {
endRunnable = animationFinishAction;
}
- dismissViewAnimated(view, endRunnable, totalDelay, 260);
+ dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
totalDelay += currentDelay;
}
@@ -5487,7 +5492,7 @@
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
final long eventStartTime;
- final View changingView;
+ final ExpandableView mChangingView;
final int animationType;
final AnimationFilter filter;
final long length;
@@ -5495,21 +5500,21 @@
int darkAnimationOriginIndex;
boolean headsUpFromBottom;
- AnimationEvent(View view, int type) {
+ AnimationEvent(ExpandableView view, int type) {
this(view, type, LENGTHS[type]);
}
- AnimationEvent(View view, int type, AnimationFilter filter) {
+ AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
this(view, type, LENGTHS[type], filter);
}
- AnimationEvent(View view, int type, long length) {
+ AnimationEvent(ExpandableView view, int type, long length) {
this(view, type, length, FILTERS[type]);
}
- AnimationEvent(View view, int type, long length, AnimationFilter filter) {
+ AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
eventStartTime = AnimationUtils.currentAnimationTimeMillis();
- changingView = view;
+ mChangingView = view;
animationType = type;
this.length = length;
this.filter = filter;
@@ -5692,7 +5697,7 @@
&& (parent.areGutsExposed()
|| mSwipeHelper.getExposedMenuView() == parent
|| (parent.getNotificationChildren().size() == 1
- && parent.isClearable()))) {
+ && parent.getEntry().isClearable()))) {
// In this case the group is expanded and showing the menu for the
// group, further interaction should apply to the group, not any
// child notifications so we use the parent of the child. We also do the same
@@ -5707,7 +5712,7 @@
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
setSwipingInProgress(true);
- mAmbientState.onBeginDrag(v);
+ mAmbientState.onBeginDrag((ExpandableView) v);
updateContinuousShadowDrawing();
requestChildrenUpdate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 87c361a..25fb7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -42,6 +42,7 @@
public class StackScrollAlgorithm {
private static final String LOG_TAG = "StackScrollAlgorithm";
+ private final ViewGroup mHostView;
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
@@ -55,7 +56,8 @@
private float mHeadsUpInset;
private int mPinnedZTranslationExtra;
- public StackScrollAlgorithm(Context context) {
+ public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+ mHostView = hostView;
initView(context);
}
@@ -79,49 +81,59 @@
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
}
- public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
+ /**
+ * Updates the state of all children in the hostview based on this algorithm.
+ */
+ public void resetViewStates(AmbientState ambientState) {
// The state of the local variables are saved in an algorithmState to easily subdivide it
// into multiple phases.
StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
// First we reset the view states to their default values.
- resultState.resetViewStates();
+ resetChildViewStates();
- initAlgorithmState(resultState, algorithmState, ambientState);
+ initAlgorithmState(mHostView, algorithmState, ambientState);
- updatePositionsForState(resultState, algorithmState, ambientState);
+ updatePositionsForState(algorithmState, ambientState);
- updateZValuesForState(resultState, algorithmState, ambientState);
+ updateZValuesForState(algorithmState, ambientState);
- updateHeadsUpStates(resultState, algorithmState, ambientState);
+ updateHeadsUpStates(algorithmState, ambientState);
- updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
- updateClipping(resultState, algorithmState, ambientState);
- updateSpeedBumpState(resultState, algorithmState, ambientState);
- updateShelfState(resultState, ambientState);
- getNotificationChildrenStates(resultState, algorithmState, ambientState);
+ updateDimmedActivatedHideSensitive(ambientState, algorithmState);
+ updateClipping(algorithmState, ambientState);
+ updateSpeedBumpState(algorithmState, ambientState);
+ updateShelfState(ambientState);
+ getNotificationChildrenStates(algorithmState, ambientState);
}
- private void getNotificationChildrenStates(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState,
+ private void resetChildViewStates() {
+ int numChildren = mHostView.getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
+ child.resetViewState();
+ }
+ }
+
+ private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView v = algorithmState.visibleChildren.get(i);
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- row.getChildrenStates(resultState, ambientState);
+ row.updateChildrenStates(ambientState);
}
}
}
- private void updateSpeedBumpState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
int belowSpeedBump = ambientState.getSpeedBumpIndex();
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ ExpandableViewState childViewState = child.getViewState();
// The speed bump can also be gone, so equality needs to be taken when comparing
// indices.
@@ -129,15 +141,16 @@
}
}
- private void updateShelfState(StackScrollState resultState, AmbientState ambientState) {
+
+ private void updateShelfState(AmbientState ambientState) {
NotificationShelf shelf = ambientState.getShelf();
if (shelf != null) {
- shelf.updateState(resultState, ambientState);
+ shelf.updateState(ambientState);
}
}
- private void updateClipping(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateClipping(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
+ ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
: 0;
@@ -146,7 +159,7 @@
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableViewState state = resultState.getViewStateForView(child);
+ ExpandableViewState state = child.getViewState();
if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
previousNotificationStart = Math.max(drawStart, previousNotificationStart);
@@ -190,15 +203,15 @@
* Updates the dimmed, activated and hiding sensitive states of the children.
*/
private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
- StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
+ StackScrollAlgorithmState algorithmState) {
boolean dimmed = ambientState.isDimmed();
boolean dark = ambientState.isFullyDark();
boolean hideSensitive = ambientState.isHideSensitive();
View activatedChild = ambientState.getActivatedChild();
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ ExpandableViewState childViewState = child.getViewState();
childViewState.dimmed = dimmed;
childViewState.dark = dark;
childViewState.hideSensitive = hideSensitive;
@@ -212,7 +225,7 @@
/**
* Initialize the algorithm state like updating the visible children.
*/
- private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+ private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
AmbientState ambientState) {
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
@@ -224,7 +237,6 @@
state.scrollY = (int) (scrollY + bottomOverScroll);
//now init the visible children and update paddings
- ViewGroup hostView = resultState.getHostView();
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
@@ -249,7 +261,7 @@
// we need normal padding now, to be in sync with what the stack calculates
lastView = null;
}
- notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+ notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
float increasedPadding = v.getIncreasedPaddingAmount();
if (increasedPadding != 0.0f) {
state.paddingMap.put(v, increasedPadding);
@@ -282,13 +294,11 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
// handle the notgoneIndex for the children as well
- List<ExpandableNotificationRow> children =
- row.getNotificationChildren();
+ List<ExpandableNotificationRow> children = row.getNotificationChildren();
if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
if (childRow.getVisibility() != View.GONE) {
- ExpandableViewState childState
- = resultState.getViewStateForView(childRow);
+ ExpandableViewState childState = childRow.getViewState();
childState.notGoneIndex = notGoneIndex;
notGoneIndex++;
}
@@ -301,8 +311,8 @@
ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
state.indexOfExpandingNotification = expandingNotification != null
? expandingNotification.isChildInGroup()
- ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
- : state.visibleChildren.indexOf(expandingNotification)
+ ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
+ : state.visibleChildren.indexOf(expandingNotification)
: -1;
}
@@ -322,10 +332,9 @@
}
}
- private int updateNotGoneIndex(StackScrollState resultState,
- StackScrollAlgorithmState state, int notGoneIndex,
+ private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
ExpandableView v) {
- ExpandableViewState viewState = resultState.getViewStateForView(v);
+ ExpandableViewState viewState = v.getViewState();
viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v);
notGoneIndex++;
@@ -335,27 +344,27 @@
/**
* Determine the positions for the views. This is the main part of the algorithm.
*
- * @param resultState The result state to update if a change to the properties of a child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * @param ambientState The current ambient state
+ * @param ambientState The current ambient state
*/
- private void updatePositionsForState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
// The y coordinate of the current child.
float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
- currentYPosition);
+ currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
}
}
- protected float updateChild(int i, StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState,
+ protected float updateChild(
+ int i,
+ StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState,
float currentYPosition) {
ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableViewState childViewState = child.getViewState();
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
@@ -404,8 +413,8 @@
return algorithmState.getPaddingAfterChild(child);
}
- private void updateHeadsUpStates(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
ExpandableNotificationRow topHeadsUpEntry = null;
for (int i = 0; i < childCount; i++) {
@@ -417,7 +426,7 @@
if (!row.isHeadsUp()) {
break;
}
- ExpandableViewState childState = resultState.getViewStateForView(row);
+ ExpandableViewState childState = row.getViewState();
if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
topHeadsUpEntry = row;
childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
@@ -439,7 +448,8 @@
childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
childState.hidden = false;
- ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+ ExpandableViewState topState =
+ topHeadsUpEntry == null ? null : topHeadsUpEntry.getViewState();
if (topState != null && !isTopEntry && (!mIsExpanded
|| unmodifiedEndLocation < topState.yTranslation + topState.height)) {
// Ensure that a headsUp doesn't vertically extend further than the heads-up at
@@ -491,9 +501,8 @@
* Clamp the height of the child down such that its end is at most on the beginning of
* the shelf.
*
- * @param child
* @param childViewState the view state of the child
- * @param ambientState the ambient state
+ * @param ambientState the ambient state
*/
private void clampPositionToShelf(ExpandableView child,
ExpandableViewState childViewState,
@@ -521,31 +530,31 @@
ExpandableView expandableView = (ExpandableView) child;
return expandableView.getIntrinsicHeight();
}
- return child == null? mCollapsedSize : child.getHeight();
+ return child == null ? mCollapsedSize : child.getHeight();
}
/**
* Calculate the Z positions for all children based on the number of items in both stacks and
* save it in the resultState
- * @param resultState The result state to update the zTranslation values
+ *
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * @param ambientState The ambient state of the algorithm
+ * @param ambientState The ambient state of the algorithm
*/
- private void updateZValuesForState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
for (int i = childCount - 1; i >= 0; i--) {
childrenOnTop = updateChildZValue(i, childrenOnTop,
- resultState, algorithmState, ambientState);
+ algorithmState, ambientState);
}
}
protected float updateChildZValue(int i, float childrenOnTop,
- StackScrollState resultState, StackScrollAlgorithmState algorithmState,
+ StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableViewState childViewState = child.getViewState();
int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
float baseZ = ambientState.getBaseZHeight();
if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
deleted file mode 100644
index e55707c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
+++ /dev/null
@@ -1,117 +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
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * A state of a
- * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which
- * can be applied to a viewGroup.
- */
-public class StackScrollState {
-
- private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
-
- private final ViewGroup mHostView;
- private WeakHashMap<ExpandableView, ExpandableViewState> mStateMap;
-
- public StackScrollState(ViewGroup hostView) {
- mHostView = hostView;
- mStateMap = new WeakHashMap<>();
- }
-
- public ViewGroup getHostView() {
- return mHostView;
- }
-
- public void resetViewStates() {
- int numChildren = mHostView.getChildCount();
- for (int i = 0; i < numChildren; i++) {
- ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
- resetViewState(child);
-
- // handling reset for child notifications
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- List<ExpandableNotificationRow> children =
- row.getNotificationChildren();
- if (row.isSummaryWithChildren() && children != null) {
- for (ExpandableNotificationRow childRow : children) {
- resetViewState(childRow);
- }
- }
- }
- }
- }
-
- private void resetViewState(ExpandableView view) {
- ExpandableViewState viewState = mStateMap.get(view);
- if (viewState == null) {
- viewState = view.createNewViewState(this);
- mStateMap.put(view, viewState);
- }
- // initialize with the default values of the view
- viewState.height = view.getIntrinsicHeight();
- viewState.gone = view.getVisibility() == View.GONE;
- viewState.alpha = 1f;
- viewState.notGoneIndex = -1;
- viewState.xTranslation = view.getTranslationX();
- viewState.hidden = false;
- viewState.scaleX = view.getScaleX();
- viewState.scaleY = view.getScaleY();
- viewState.inShelf = false;
- viewState.headsUpIsVisible = false;
- }
-
- public ExpandableViewState getViewStateForView(View requestedView) {
- return mStateMap.get(requestedView);
- }
-
- public void removeViewStateForView(View child) {
- mStateMap.remove(child);
- }
-
- /**
- * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
- * The properties are only applied if they effectively changed.
- */
- public void apply() {
- int numChildren = mHostView.getChildCount();
- for (int i = 0; i < numChildren; i++) {
- ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
- ExpandableViewState state = mStateMap.get(child);
- if (state == null) {
- Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
- "to the hostView");
- continue;
- }
- if (state.gone) {
- continue;
- }
- state.applyToView(child);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 34dab53..d690547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -44,6 +44,7 @@
public static final int ANIMATION_DURATION_WAKEUP = 500;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
+ public static final int ANIMATION_DURATION_SWIPE = 260;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
@@ -128,25 +129,25 @@
public void startAnimationForEvents(
ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
- StackScrollState finalState, long additionalDelay) {
+ long additionalDelay) {
- processAnimationEvents(mAnimationEvents, finalState);
+ processAnimationEvents(mAnimationEvents);
int childCount = mHostLayout.getChildCount();
mAnimationFilter.applyCombination(mNewEvents);
mCurrentAdditionalDelay = additionalDelay;
mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
- mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
+ mCurrentLastNotAddedIndex = findLastNotAddedIndex();
for (int i = 0; i < childCount; i++) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
- ExpandableViewState viewState = finalState.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
if (viewState == null || child.getVisibility() == View.GONE
- || applyWithoutAnimation(child, viewState, finalState)) {
+ || applyWithoutAnimation(child, viewState)) {
continue;
}
- initAnimationProperties(finalState, child, viewState);
+ initAnimationProperties(child, viewState);
viewState.animateTo(child, mAnimationProperties);
}
if (!isRunning()) {
@@ -159,7 +160,7 @@
mNewAddChildren.clear();
}
- private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
+ private void initAnimationProperties(ExpandableView child,
ExpandableViewState viewState) {
boolean wasAdded = mAnimationProperties.wasAdded(child);
mAnimationProperties.duration = mCurrentLength;
@@ -173,7 +174,7 @@
|| viewState.clipTopAmount != child.getClipTopAmount()
|| viewState.dark != child.isDark())) {
mAnimationProperties.delay = mCurrentAdditionalDelay
- + calculateChildAnimationDelay(viewState, finalState);
+ + calculateChildAnimationDelay(viewState);
}
}
@@ -193,8 +194,7 @@
*
* @return true if no animation should be performed
*/
- private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
- StackScrollState finalState) {
+ private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState) {
if (mShadeExpanded) {
return false;
}
@@ -214,12 +214,12 @@
return true;
}
- private int findLastNotAddedIndex(StackScrollState finalState) {
+ private int findLastNotAddedIndex() {
int childCount = mHostLayout.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
- ExpandableViewState viewState = finalState.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
if (viewState == null || child.getVisibility() == View.GONE) {
continue;
}
@@ -230,8 +230,7 @@
return -1;
}
- private long calculateChildAnimationDelay(ExpandableViewState viewState,
- StackScrollState finalState) {
+ private long calculateChildAnimationDelay(ExpandableViewState viewState) {
if (mAnimationFilter.hasGoToFullShadeEvent) {
return calculateDelayGoToFullShade(viewState);
}
@@ -244,8 +243,8 @@
switch (event.animationType) {
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
int ownIndex = viewState.notGoneIndex;
- int changingIndex = finalState
- .getViewStateForView(event.changingView).notGoneIndex;
+ int changingIndex =
+ ((ExpandableView) (event.mChangingView)).getViewState().notGoneIndex;
int difference = Math.abs(ownIndex - changingIndex);
difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
difference - 1));
@@ -258,9 +257,9 @@
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
int ownIndex = viewState.notGoneIndex;
boolean noNextView = event.viewAfterChangingView == null;
- View viewAfterChangingView = noNextView
+ ExpandableView viewAfterChangingView = noNextView
? mHostLayout.getLastChildNotGone()
- : event.viewAfterChangingView;
+ : (ExpandableView) event.viewAfterChangingView;
if (viewAfterChangingView == null) {
// This can happen when the last view in the list is removed.
// Since the shelf is still around and the only view, the code still goes
@@ -268,8 +267,7 @@
// have changed.
continue;
}
- int nextIndex = finalState
- .getViewStateForView(viewAfterChangingView).notGoneIndex;
+ int nextIndex = viewAfterChangingView.getViewState().notGoneIndex;
if (ownIndex >= nextIndex) {
// we only have the view afterwards
ownIndex++;
@@ -351,20 +349,17 @@
/**
* Process the animationEvents for a new animation
*
- * @param animationEvents the animation events for the animation to perform
- * @param finalState the final state to animate to
+ * @param animationEvents the animation events for the animation to perform
*/
private void processAnimationEvents(
- ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
- StackScrollState finalState) {
+ ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
- final ExpandableView changingView = (ExpandableView) event.changingView;
+ final ExpandableView changingView = (ExpandableView) event.mChangingView;
if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
// This item is added, initialize it's properties.
- ExpandableViewState viewState = finalState
- .getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
if (viewState == null || viewState.gone) {
// The position for this child was never generated, let's continue.
continue;
@@ -381,12 +376,9 @@
// Find the amount to translate up. This is needed in order to understand the
// direction of the remove animation (either downwards or upwards)
- ExpandableViewState viewState = finalState
- .getViewStateForView(event.viewAfterChangingView);
- int actualHeight = changingView.getActualHeight();
// upwards by default
float translationDirection = -1.0f;
- if (viewState != null) {
+ if (event.viewAfterChangingView != null) {
float ownPosition = changingView.getTranslationY();
if (changingView instanceof ExpandableNotificationRow
&& event.viewAfterChangingView instanceof ExpandableNotificationRow) {
@@ -402,8 +394,11 @@
ownPosition = changingRow.getTranslationWhenRemoved();
}
}
+ int actualHeight = changingView.getActualHeight();
// there was a view after this one, Approximate the distance the next child
// travelled
+ ExpandableViewState viewState =
+ ((ExpandableView) event.viewAfterChangingView).getViewState();
translationDirection = ((viewState.yTranslation
- (ownPosition + actualHeight / 2.0f)) * 2 /
actualHeight);
@@ -426,11 +421,11 @@
}
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
- row.prepareExpansionChanged(finalState);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
+ row.prepareExpansionChanged();
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
- ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
if (viewState != null) {
mTmpState.copyFrom(viewState);
mTmpState.yTranslation += mPulsingAppearingTranslation;
@@ -439,7 +434,7 @@
}
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
- ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
if (viewState != null) {
viewState.alpha = 0;
// We want to animate the alpha away before the view starts translating,
@@ -454,7 +449,7 @@
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
// This item is added, initialize it's properties.
- ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
mTmpState.copyFrom(viewState);
if (event.headsUpFromBottom) {
mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
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/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 40f9f45..3c8cad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -143,9 +143,9 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
updateTopEntry();
- updateHeader(headsUp.getEntry());
+ updateHeader(entry);
}
/** To count the distance from the window right boundary to scroller right boundary. The
@@ -298,9 +298,9 @@
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
updateTopEntry();
- updateHeader(headsUp.getEntry());
+ updateHeader(entry);
}
public void setExpandedHeight(float expandedHeight, float appearFraction) {
@@ -339,7 +339,7 @@
}
public void updateHeader(NotificationData.Entry entry) {
- ExpandableNotificationRow row = entry.row;
+ ExpandableNotificationRow row = entry.getRow();
float headerVisibleAmount = 1.0f;
if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
headerVisibleAmount = mExpandFraction;
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 00d6b14..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() {
@@ -304,18 +304,19 @@
return;
}
if (hasPinnedHeadsUp()) {
- ExpandableNotificationRow topEntry = getTopEntry().row;
+ NotificationData.Entry topEntry = getTopEntry();
if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ final NotificationData.Entry groupSummary
+ = mGroupManager.getGroupSummary(topEntry.notification);
if (groupSummary != null) {
topEntry = groupSummary;
}
}
- topEntry.getLocationOnScreen(mTmpTwoArray);
+ ExpandableNotificationRow topRow = topEntry.getRow();
+ topRow.getLocationOnScreen(mTmpTwoArray);
int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int height = topEntry.getIntrinsicHeight();
+ int maxX = mTmpTwoArray[0] + topRow.getWidth();
+ int height = topRow.getIntrinsicHeight();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index be4df45..9c1c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -84,8 +84,8 @@
// We might touch above the visible heads up child, but then we still would
// like to capture it.
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
- if (topEntry != null && topEntry.row.isPinned()) {
- mPickedChild = topEntry.row;
+ if (topEntry != null && topEntry.isRowPinned()) {
+ mPickedChild = topEntry.getRow();
mTouchingHeadsUpView = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c0d1818..7d13679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -227,6 +227,7 @@
mShowingSoon = false;
if (mExpansion == EXPANSION_VISIBLE) {
mKeyguardView.onResume();
+ mKeyguardView.resetSecurityContainer();
}
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d8280ba..a81b7e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -22,6 +22,7 @@
import android.content.res.Resources;
import android.util.MathUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -229,6 +230,11 @@
- mBurnInPreventionOffsetX;
}
+ @VisibleForTesting
+ void setPulsingPadding(int padding) {
+ mPulsingPadding = padding;
+ }
+
public static class Result {
/**
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 1002f9e..b83ebc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-
import android.annotation.NonNull;
import android.hardware.input.InputManager;
import android.os.Handler;
@@ -35,10 +33,8 @@
*/
public class NavigationBackAction extends NavigationGestureAction {
- private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback";
private static final String BACK_AFTER_END_PROP =
"quickstepcontroller_homegoesbackwhenend";
- private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
@@ -60,23 +56,13 @@
}
@Override
- public int requiresTouchDownHitTarget() {
- return HIT_TARGET_HOME;
- }
-
- @Override
- public boolean requiresDragWithHitTarget() {
- return true;
- }
-
- @Override
public boolean canPerformAction() {
return mProxySender.getBackButtonAlpha() > 0;
}
@Override
public boolean isEnabled() {
- return swipeHomeGoBackGestureEnabled();
+ return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED);
}
@Override
@@ -110,13 +96,8 @@
mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
- private boolean swipeHomeGoBackGestureEnabled() {
- return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
- && getGlobalBoolean(PULL_HOME_GO_BACK_PROP);
- }
-
private boolean shouldExecuteBackOnUp() {
- return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
+ return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED)
&& getGlobalBoolean(BACK_AFTER_END_PROP);
}
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 0cf1b3d..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;
@@ -56,7 +55,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -91,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;
@@ -102,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;
@@ -200,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);
@@ -226,7 +225,6 @@
@Override
public void onDestroy() {
super.onDestroy();
- mCommandQueue.removeCallbacks(this);
Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
@@ -863,16 +861,9 @@
public static View create(Context context, FragmentListener listener) {
final int displayId = context.getDisplay().getDisplayId();
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- final int height = isDefaultDisplay
- ? LayoutParams.MATCH_PARENT
- : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, height,
- // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
- isDefaultDisplay
- ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
- : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -884,10 +875,6 @@
lp.setTitle("NavigationBar" + displayId);
lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
- if (!isDefaultDisplay) {
- lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
- lp.gravity = Gravity.BOTTOM;
- }
View navigationBarView = LayoutInflater.from(context).inflate(
R.layout.navigation_bar_window, null);
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 5db43ea..cd6e1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
@@ -77,6 +78,8 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -146,6 +149,8 @@
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
+ private NavigationPrototypeController mPrototypeController;
+ private NavigationGestureAction[] mDefaultGestureMap;
private QuickScrubAction mQuickScrubAction;
private QuickStepAction mQuickStepAction;
private NavigationBackAction mBackAction;
@@ -261,6 +266,18 @@
}
};
+ private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
+ @Override
+ public void onGestureRemap(int[] actions) {
+ updateNavigationGestures();
+ }
+
+ @Override
+ public void onBackButtonVisibilityChanged(boolean visible) {
+ getBackButton().setVisibility(visible ? VISIBLE : GONE);
+ }
+ };
+
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -309,6 +326,14 @@
mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
mBackAction = new NavigationBackAction(this, mOverviewProxyService);
+ mDefaultGestureMap = new NavigationGestureAction[] {
+ mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
+ mQuickScrubAction
+ };
+
+ mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
+ mPrototypeController.register();
+ mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
}
public BarTransitions getBarTransitions() {
@@ -323,8 +348,32 @@
mPanelView = panel;
if (mGestureHelper instanceof QuickStepController) {
((QuickStepController) mGestureHelper).setComponents(this);
- ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction,
- null /* swipeDownAction*/, mBackAction, mQuickScrubAction);
+ updateNavigationGestures();
+ }
+ }
+
+ private void updateNavigationGestures() {
+ if (mGestureHelper instanceof QuickStepController) {
+ final int[] assignedMap = mPrototypeController.getGestureActionMap();
+ ((QuickStepController) mGestureHelper).setGestureActions(
+ getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
+ getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
+ getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
+ getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
+ }
+ }
+
+ private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
+ NavigationGestureAction defaultAction) {
+ switch(actionType) {
+ case NavigationPrototypeController.ACTION_QUICKSTEP:
+ return mQuickStepAction;
+ case NavigationPrototypeController.ACTION_QUICKSCRUB:
+ return mQuickScrubAction;
+ case NavigationPrototypeController.ACTION_BACK:
+ return mBackAction;
+ default:
+ return defaultAction;
}
}
@@ -902,10 +951,10 @@
private void updateTaskSwitchHelper() {
if (mGestureHelper == null) return;
boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
- int navBarPos = 0;
+ int navBarPos = NAV_BAR_INVALID;
try {
- // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
- navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
+ mDisplay.getDisplayId());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get nav bar position.", e);
}
@@ -1043,6 +1092,7 @@
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
+ mPrototypeController.unregister();
setUpSwipeUpOnboarding(false);
for (int i = 0; i < mButtonDispatchers.size(); ++i) {
mButtonDispatchers.valueAt(i).onDestroy();
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 83067f6..a8d00c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -127,6 +127,15 @@
}
/**
+ * Decide if the controller should not send the current motion event to launcher via
+ * {@link OverviewProxyService}
+ * @return if controller should not proxy
+ */
+ public boolean disableProxyEvents() {
+ return false;
+ }
+
+ /**
* 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.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
new file mode 100644
index 0000000..e8c0bf1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
+ * prototypes to run in the system. The class will handle communication changes from the settings
+ * app and call back to listeners.
+ */
+public class NavigationPrototypeController extends ContentObserver {
+ private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
+
+ static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
+ private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
+ @interface GestureAction {}
+ static final int ACTION_DEFAULT = 0;
+ static final int ACTION_QUICKSTEP = 1;
+ static final int ACTION_QUICKSCRUB = 2;
+ static final int ACTION_BACK = 3;
+
+ private OnPrototypeChangedListener mListener;
+
+ /**
+ * Each index corresponds to a different action set in QuickStepController
+ * {@see updateSwipeLTRBackSetting}
+ */
+ private int[] mActionMap = new int[4];
+
+ private final Context mContext;
+
+ public NavigationPrototypeController(Handler handler, Context context) {
+ super(handler);
+ mContext = context;
+ updateSwipeLTRBackSetting();
+ }
+
+ public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Observe all the settings to react to from prototype settings
+ */
+ public void register() {
+ registerObserver(HIDE_BACK_BUTTON_SETTING);
+ registerObserver(GESTURE_MATCH_SETTING);
+ }
+
+ /**
+ * Disable observing settings to react to from prototype settings
+ */
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (!selfChange && mListener != null) {
+ try {
+ final String path = uri.getPath();
+ if (path.endsWith(GESTURE_MATCH_SETTING)) {
+ // Get the settings gesture map corresponding to each action
+ // {@see updateSwipeLTRBackSetting}
+ updateSwipeLTRBackSetting();
+ mListener.onGestureRemap(mActionMap);
+ } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
+ mListener.onBackButtonVisibilityChanged(
+ !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+ }
+ } catch (SettingNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Retrieve the action map to apply to the quick step controller
+ * @return an action map
+ */
+ int[] getGestureActionMap() {
+ return mActionMap;
+ }
+
+ /**
+ * Since Settings.Global cannot pass arrays, use a string to represent each character as a
+ * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
+ * Number: [up] [down] [left] [right]
+ */
+ private void updateSwipeLTRBackSetting() {
+ String value = Settings.Global.getString(mContext.getContentResolver(),
+ GESTURE_MATCH_SETTING);
+ if (value != null) {
+ for (int i = 0; i < mActionMap.length; ++i) {
+ mActionMap[i] = Character.getNumericValue(value.charAt(i));
+ }
+ }
+ }
+
+ private boolean getGlobalBool(String name) throws SettingNotFoundException {
+ return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
+ }
+
+ private void registerObserver(String name) {
+ mContext.getContentResolver()
+ .registerContentObserver(Settings.Global.getUriFor(name), false, this);
+ }
+
+ public interface OnPrototypeChangedListener {
+ void onGestureRemap(@GestureAction int[] actions);
+ void onBackButtonVisibilityChanged(boolean visible);
+ }
+}
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 c74514e..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);
}
/**
@@ -198,7 +198,7 @@
alertNotificationWhenPossible(entry, getActiveAlertManager());
} else {
// The transfer is no longer valid. Free the content.
- entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+ entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
}
}
}
@@ -299,9 +299,9 @@
Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
if (child != null) {
- if (child.row.keepInParent()
- || child.row.isRemoved()
- || child.row.isDismissed()) {
+ if (child.getRow().keepInParent()
+ || child.isRowRemoved()
+ || child.isRowDismissed()) {
// The notification is actually already removed. No need to alert it.
return;
}
@@ -390,10 +390,10 @@
private void alertNotificationWhenPossible(@NonNull Entry entry,
@NonNull AlertingNotificationManager alertManager) {
@InflationFlag int contentFlag = alertManager.getContentFlag();
- if (!entry.row.isInflationFlagSet(contentFlag)) {
+ if (!entry.getRow().isInflationFlagSet(contentFlag)) {
mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
- entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
- entry.row.inflateViews();
+ entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+ entry.getRow().inflateViews();
return;
}
if (alertManager.isAlerting(entry.key)) {
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 8ceabf8..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);
}
/**
@@ -87,8 +87,7 @@
group.expanded = expanded;
if (group.summary != null) {
for (OnGroupChangeListener listener : mListeners) {
- listener.onGroupExpansionChanged(group.summary.row,
- expanded);
+ listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
}
}
}
@@ -133,7 +132,7 @@
}
public void onEntryAdded(final NotificationData.Entry added) {
- if (added.row.isRemoved()) {
+ if (added.isRowRemoved()) {
added.setDebugThrowable(new Throwable());
}
final StatusBarNotification sbn = added.notification;
@@ -152,17 +151,17 @@
if (existing != null && existing != added) {
Throwable existingThrowable = existing.getDebugThrowable();
Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
- + "existing removed: " + existing.row.isRemoved()
+ + "existing removed: " + existing.isRowRemoved()
+ (existingThrowable != null
? Log.getStackTraceString(existingThrowable) + "\n": "")
- + " added removed" + added.row.isRemoved()
+ + " added removed" + added.isRowRemoved()
, new Throwable());
}
group.children.put(added.key, added);
updateSuppression(group);
} else {
group.summary = added;
- group.expanded = added.row.areChildrenExpanded();
+ group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
ArrayList<NotificationData.Entry> childrenCopy
@@ -263,9 +262,9 @@
if (!isOnlyChild(sbn)) {
return false;
}
- ExpandableNotificationRow logicalGroupSummary = getLogicalGroupSummary(sbn);
+ NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
return logicalGroupSummary != null
- && !logicalGroupSummary.getStatusBarNotification().equals(sbn);
+ && !logicalGroupSummary.notification.equals(sbn);
}
private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,7 +338,7 @@
* Get the summary of a specified status bar notification. For isolated notification this return
* itself.
*/
- public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
+ public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(getGroupKey(sbn));
}
@@ -348,16 +347,17 @@
* but the logical summary, i.e when a child is isolated, it still returns the summary as if
* it wasn't isolated.
*/
- public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
+ public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(sbn.getGroupKey());
}
@Nullable
- private ExpandableNotificationRow getGroupSummary(String groupKey) {
+ private NotificationData.Entry getGroupSummary(String groupKey) {
NotificationGroup group = mGroupMap.get(groupKey);
+ //TODO: see if this can become an Entry
return group == null ? null
: group.summary == null ? null
- : group.summary.row;
+ : group.summary;
}
/**
@@ -438,11 +438,11 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
}
@Override
@@ -533,8 +533,7 @@
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
return notificationGroup.summary == null
- || notificationGroup.summary.row.getClipTopAmount() > 0
- || notificationGroup.summary.row.getTranslationY() < 0;
+ || notificationGroup.summary.isGroupNotFullyVisible();
}
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 184766c..2d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -192,13 +192,13 @@
&& !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
return false;
}
- if (!StatusBar.isTopLevelChild(entry)) {
+ if (!entry.isTopLevelChild()) {
return false;
}
- if (entry.row.getVisibility() == View.GONE) {
+ if (entry.getRow().getVisibility() == View.GONE) {
return false;
}
- if (entry.row.isDismissed() && hideDismissed) {
+ if (entry.isRowDismissed() && hideDismissed) {
return false;
}
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 851e6d0..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);
}
@@ -2500,25 +2500,26 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && headsUp.isHeadsUp()) {
- mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
- headsUp.setHeadsUpIsVisible();
+ if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(
+ entry.getHeadsUpAnimationView(), false);
+ entry.setHeadsUpIsVisible();
}
}
@Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+ mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
}
@Override
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 74744f1..2b202eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
@@ -212,6 +212,11 @@
}
@Override
+ public boolean disableProxyEvents() {
+ return true;
+ }
+
+ @Override
protected void onGestureStart(MotionEvent event) {
updateHighlight();
ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
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 0eff4d4..4983618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -30,9 +31,9 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
@@ -100,6 +101,7 @@
private NavigationGestureAction mCurrentAction;
private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
+ private final Rect mLastLayoutRect = new Rect();
private final OverviewProxyService mOverviewEventSender;
private final Context mContext;
private final StatusBar mStatusBar;
@@ -107,7 +109,6 @@
private final Matrix mTransformLocalMatrix = new Matrix();
public QuickStepController(Context context) {
- final Resources res = context.getResources();
mContext = context;
mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
@@ -142,6 +143,8 @@
if (action != null) {
action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
action.onDarkIntensityChange(mDarkIntensity);
+ action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
+ mLastLayoutRect.right, mLastLayoutRect.bottom);
}
}
}
@@ -182,6 +185,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;
}
@@ -272,25 +276,21 @@
if (mDragVPositive ? (posV < touchDownV) : (posV > touchDownV)) {
// Swiping up gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_UP_INDEX],
- false /* alignedWithNavBar */, false /* positiveDirection */,
- event);
+ false /* alignedWithNavBar */, event);
} else {
// Swiping down gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_DOWN_INDEX],
- false /* alignedWithNavBar */, true /* positiveDirection */,
- event);
+ false /* alignedWithNavBar */, event);
}
} else if (exceededSwipeHorizontalTouchSlop) {
if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
// Swiping left (ltr) gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
- true /* alignedWithNavBar */, false /* positiveDirection */,
- event);
+ true /* alignedWithNavBar */, event);
} else {
// Swiping right (ltr) gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
- true /* alignedWithNavBar */, true /* positiveDirection */,
- event);
+ true /* alignedWithNavBar */, event);
}
}
}
@@ -303,7 +303,6 @@
case MotionEvent.ACTION_UP:
if (mCurrentAction != null) {
mCurrentAction.endGesture();
- mCurrentAction = null;
}
// Return the hit target back to its original position
@@ -326,6 +325,11 @@
if (shouldProxyEvents(action)) {
proxyMotionEvents(event);
}
+
+ // Clear action when gesture and event proxy finishes
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mCurrentAction = null;
+ }
return mCurrentAction != null || deadZoneConsumed;
}
@@ -351,8 +355,7 @@
private boolean shouldProxyEvents(int action) {
final boolean actionValid = (mCurrentAction == null
- || (mGestureActions[ACTION_SWIPE_UP_INDEX] != null
- && mGestureActions[ACTION_SWIPE_UP_INDEX].isActive()));
+ || !mCurrentAction.disableProxyEvents());
if (actionValid && !mIsInScreenPinning) {
// Allow down, cancel and up events, move and other events are passed if notifications
// are not showing and disabled gestures (such as long press) are not executed
@@ -382,6 +385,7 @@
action.onLayout(changed, left, top, right, bottom);
}
}
+ mLastLayoutRect.set(left, top, right, bottom);
}
@Override
@@ -418,6 +422,9 @@
mDragHPositive = !isRTL;
mDragVPositive = true;
break;
+ case NAV_BAR_INVALID:
+ Log.e(TAG, "Invalid nav bar position");
+ break;
}
for (NavigationGestureAction action: mGestureActions) {
@@ -448,7 +455,7 @@
}
private void tryToStartGesture(NavigationGestureAction action, boolean alignedWithNavBar,
- boolean positiveDirection, MotionEvent event) {
+ MotionEvent event) {
if (action == null) {
return;
}
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 bdddf5b..bf53b77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -22,6 +22,7 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -160,7 +161,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;
@@ -187,6 +187,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;
@@ -195,7 +196,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -474,8 +474,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);
@@ -484,7 +486,7 @@
private Runnable mLaunchTransitionEndRunnable;
protected boolean mLaunchTransitionFadingAway;
- private ExpandableNotificationRow mDraggedDownRow;
+ private NotificationData.Entry mDraggedDownEntry;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private int mLastCameraLaunchSource;
@@ -577,7 +579,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 +631,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 +667,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<>();
@@ -721,7 +726,7 @@
IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
try {
- wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
+ wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
} catch (RemoteException e) {
// Just pass, nothing critical.
}
@@ -843,16 +848,7 @@
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
-
- try {
- boolean showNav = mWindowManagerService.hasNavigationBar();
- if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
- if (showNav) {
- createNavigationBar();
- }
- } catch (RemoteException ex) {
- // no window manager? good luck with that
- }
+ createNavigationBar();
if (ENABLE_LOCKSCREEN_WALLPAPER) {
mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
@@ -927,8 +923,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);
@@ -1016,15 +1011,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);
+
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);
}
/**
@@ -1061,13 +1070,24 @@
}
protected void createNavigationBar() {
- mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
- mNavigationBar = (NavigationBarFragment) fragment;
- if (mLightBarController != null) {
- mNavigationBar.setLightBarController(mLightBarController);
- }
- mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
- });
+ try {
+ // TODO(117478341): Move this into DisplayNavigationBarController#createNavigationBars
+ // for-loop. We will also move the whole navigation bar logic together.
+ final boolean showNav = mWindowManagerService.hasNavigationBar(DEFAULT_DISPLAY);
+ if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
+ if (!showNav) return;
+
+ mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
+ mNavigationBar = (NavigationBarFragment) fragment;
+ if (mLightBarController != null) {
+ mNavigationBar.setLightBarController(mLightBarController);
+ }
+ mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
+ });
+ } catch (RemoteException ex) {
+ // no window manager? good luck with that
+ }
+ mNavigationBarController.createNavigationBars();
}
/**
@@ -1209,7 +1229,8 @@
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+ final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
+ mDisplay.getDisplayId());
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
}
@@ -1265,10 +1286,6 @@
mQSPanel.clickTile(tile);
}
- public static boolean isTopLevelChild(Entry entry) {
- return entry.row.getParent() instanceof NotificationStackScrollLayout;
- }
-
public boolean areNotificationsHidden() {
return mZenController.areNotificationsHiddenInShade();
}
@@ -1491,12 +1508,12 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
dismissVolumeDialog();
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
}
@Override
@@ -1541,7 +1558,7 @@
}
public boolean isPulsing() {
- return mAmbientPulseManager.hasNotifications();
+ return mPulsing;
}
public boolean isLaunchTransitionFadingAway() {
@@ -1635,9 +1652,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 */);
@@ -1936,7 +1953,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");
@@ -2523,7 +2540,7 @@
}
}
if (dismissShade) {
- if (mExpandedVisible) {
+ if (mExpandedVisible && !mBouncerShowing) {
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
true /* delayed*/);
} else {
@@ -2605,9 +2622,7 @@
final int notificationCount = activeNotifications.size();
for (int i = 0; i < notificationCount; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
- if (entry.row != null) {
- entry.row.resetUserExpansion();
- }
+ entry.resetUserExpansion();
}
}
@@ -2888,7 +2903,7 @@
mContext.unregisterReceiver(mDemoReceiver);
mAssistManager.destroy();
mHeadsUpManager.destroy();
- mStatusBarStateController.removeListener(this);
+ mStatusBarStateController.removeCallback(this);
if (mQSPanel != null && mQSPanel.getHost() != null) {
mQSPanel.getHost().destroy();
@@ -3038,10 +3053,10 @@
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
- if (mDraggedDownRow != null) {
- mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
- mDraggedDownRow = null;
+ if (mDraggedDownEntry != null) {
+ mDraggedDownEntry.setUserLocked(false);
+ mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
+ mDraggedDownEntry = null;
}
}
@@ -3181,9 +3196,9 @@
}
long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
mNotificationPanel.animateToFullShade(delay);
- if (mDraggedDownRow != null) {
- mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow = null;
+ if (mDraggedDownEntry != null) {
+ mDraggedDownEntry.setUserLocked(false);
+ mDraggedDownEntry = null;
}
// TODO(115978725): Support animations on external nav bars.
@@ -3206,6 +3221,7 @@
mNotificationPanel.onAffordanceLaunchEnded();
mNotificationPanel.animate().cancel();
mNotificationPanel.setAlpha(1f);
+ updateScrimController();
Trace.endSection();
return staying;
}
@@ -3575,14 +3591,15 @@
int userId = mLockscreenUserManager.getCurrentUserId();
ExpandableNotificationRow row = null;
+ NotificationData.Entry entry = null;
if (expandView instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) expandView;
- row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+ entry = ((ExpandableNotificationRow) expandView).getEntry();
+ entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
- row.setGroupExpansionChanging(true);
- if (row.getStatusBarNotification() != null) {
- userId = row.getStatusBarNotification().getUserId();
+ entry.setGroupExpansionChanging(true);
+ if (entry.notification != null) {
+ userId = entry.notification.getUserId();
}
}
boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
@@ -3592,7 +3609,7 @@
if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
showBouncerIfKeyguard();
- mDraggedDownRow = row;
+ mDraggedDownEntry = entry;
mPendingRemoteInputView = null;
} else {
mNotificationPanel.animateToFullShade(0 /* delay */);
@@ -3933,6 +3950,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() {
@@ -3946,6 +3967,7 @@
@Override
public void onPulseFinished() {
+ mPulsing = false;
callback.onPulseFinished();
setPulsing(false);
}
@@ -4326,7 +4348,14 @@
}, afterKeyguardGone);
}
+ @Override
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+ startPendingIntentDismissingKeyguard(intent, null);
+ }
+
+ @Override
+ public void startPendingIntentDismissingKeyguard(
+ final PendingIntent intent, @Nullable final Runnable intentSentCallback) {
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
@@ -4345,6 +4374,9 @@
if (intent.isActivity()) {
mAssistManager.hideAssist();
}
+ if (intentSentCallback != null) {
+ intentSentCallback.run();
+ }
}, afterKeyguardGone);
}
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 edfc049..3550bd6 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,10 @@
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.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 +49,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,9 +62,7 @@
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.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.PreviewInflater;
public class StatusBarNotificationPresenter implements NotificationPresenter {
@@ -93,7 +73,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 +84,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,7 +95,6 @@
private final CommandQueue mCommandQueue;
private final AccessibilityManager mAccessibilityManager;
- private final LockPatternUtils mLockPatternUtils;
private final KeyguardManager mKeyguardManager;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private final int mMaxAllowedKeyguardNotifications;
@@ -133,34 +105,32 @@
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) {
mContext = context;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mCommandQueue = getComponent(context, CommandQueue.class);
mAboveShelfObserver = new AboveShelfObserver(stackScroller);
+ mActivityLaunchAnimator = activityLaunchAnimator;
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 +146,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;
@@ -204,11 +176,6 @@
}
@Override
- public ActivityLaunchAnimator getActivityLaunchAnimator() {
- return mActivityLaunchAnimator;
- }
-
- @Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
|| mActivityLaunchAnimator.isAnimationPending()
@@ -216,11 +183,6 @@
}
@Override
- public boolean isCollapsingToShowActivityOverLockscreen() {
- return mIsCollapsingToShowActivityOverLockscreen;
- }
-
- @Override
public void onPerformRemoveNotification(StatusBarNotification n) {
if (mNotificationPanel.hasPulsingNotifications() &&
!mAmbientPulseManager.hasNotifications()) {
@@ -377,201 +339,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).getStatusBarNotification();
- 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,
@@ -591,7 +358,7 @@
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
- mShadeController.goToLockedShade(clickedEntry.row);
+ mShadeController.goToLockedShade(clickedEntry.getRow());
}
}
@@ -600,41 +367,12 @@
return mVrMode;
}
- @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();
- }
-
private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
mActivityStarter.dismissKeyguardThenExecute(dismissAction, null,
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 +383,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 62b6d91..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);
}
@@ -283,7 +283,6 @@
applyModalFlag(state);
applyBrightness(state);
applyHasTopUi(state);
- applySleepToken(state);
applyNotTouchable(state);
if (mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mStatusBarView, mLp);
@@ -328,14 +327,6 @@
mHasTopUiChanged = isExpanded(state);
}
- private void applySleepToken(State state) {
- if (state.dozing) {
- mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
- } else {
- mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
- }
- }
-
private void applyNotTouchable(State state) {
if (state.notTouchable) {
mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 978a72d..53e461d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -49,6 +49,7 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.view.WindowInsetsController;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
@@ -785,6 +786,11 @@
@Override
public void reportActivityRelaunched() {
}
+
+ @Override
+ public WindowInsetsController getInsetsController() {
+ return null;
+ }
};
}
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/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fdab616..e7280643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -123,15 +123,15 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setEntryPinned: " + isPinned);
}
- ExpandableNotificationRow row = headsUpEntry.mEntry.row;
- if (row.isPinned() != isPinned) {
- row.setPinned(isPinned);
+ NotificationData.Entry entry = headsUpEntry.mEntry;
+ if (entry.isRowPinned() != isPinned) {
+ entry.setRowPinned(isPinned);
updatePinnedMode();
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
- listener.onHeadsUpPinned(row);
+ listener.onHeadsUpPinned(entry);
} else {
- listener.onHeadsUpUnPinned(row);
+ listener.onHeadsUpUnPinned(entry);
}
}
}
@@ -144,7 +144,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setHeadsUp(true);
+ entry.setHeadsUp(true);
setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, true);
@@ -154,12 +154,12 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setHeadsUp(false);
+ entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
protected void updatePinnedMode() {
@@ -282,7 +282,7 @@
private boolean hasPinnedNotificationInternal() {
for (String key : mAlertEntries.keySet()) {
AlertEntry entry = getHeadsUpEntry(key);
- if (entry.mEntry.row.isPinned()) {
+ if (entry.mEntry.isRowPinned()) {
return true;
}
}
@@ -302,10 +302,9 @@
// when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
// on the screen.
- if (userUnPinned && entry.mEntry != null && entry.mEntry.row != null) {
- ExpandableNotificationRow row = entry.mEntry.row;
- if (row.mustStayOnScreen()) {
- row.setHeadsUpIsVisible();
+ if (userUnPinned && entry.mEntry != null) {
+ if (entry.mEntry.mustStayOnScreen()) {
+ entry.mEntry.setHeadsUpIsVisible();
}
}
}
@@ -341,7 +340,7 @@
*/
public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
- if (headsUpEntry != null && entry.row.isPinned()) {
+ if (headsUpEntry != null && entry.isRowPinned()) {
headsUpEntry.setExpanded(expanded);
}
}
@@ -365,15 +364,15 @@
@Override
protected boolean isSticky() {
- return (mEntry.row.isPinned() && expanded)
+ return (mEntry.isRowPinned() && expanded)
|| remoteInputActive || hasFullScreenIntent(mEntry);
}
@Override
public int compareTo(@NonNull AlertEntry alertEntry) {
HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
- boolean isPinned = mEntry.row.isPinned();
- boolean otherPinned = headsUpEntry.mEntry.row.isPinned();
+ boolean isPinned = mEntry.isRowPinned();
+ boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
if (isPinned && !otherPinned) {
return -1;
} else if (!isPinned && otherPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index d434768..7ad547a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -33,12 +33,12 @@
/**
* A notification was just pinned to the top.
*/
- default void onHeadsUpPinned(ExpandableNotificationRow headsUp) {}
+ default void onHeadsUpPinned(NotificationData.Entry entry) {}
/**
* A notification was just unpinned from the top.
*/
- default void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {}
+ default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
/**
* A notification just became a heads up or turned back to its normal state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a485fa8..866015e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -245,7 +245,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (mEntry.row.isChangingPosition()) {
+ if (mEntry.getRow().isChangingPosition()) {
if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
mEditText.requestFocus();
}
@@ -255,7 +255,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
+ if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
mController.removeRemoteInput(mEntry, mToken);
@@ -495,7 +495,7 @@
}
private void defocusIfNeeded(boolean animate) {
- if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
+ if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
|| isTemporarilyDetached()) {
if (isTemporarilyDetached()) {
// We might get reattached but then the other one of HUN / expanded might steal
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 0186683..2a4336e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.policy;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
@@ -20,7 +21,6 @@
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;
@@ -40,6 +40,7 @@
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;
@@ -189,15 +190,13 @@
* into the notification are shown.
*/
public void addRepliesFromRemoteInput(
- RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry,
- CharSequence[] choices) {
- if (remoteInput != null && pendingIntent != null) {
- if (choices != null) {
- for (int i = 0; i < choices.length; ++i) {
+ SmartReplies smartReplies,
+ SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
+ if (smartReplies.choices != null) {
+ for (int i = 0; i < smartReplies.choices.length; ++i) {
Button replyButton = inflateReplyButton(
- getContext(), this, i, choices[i], remoteInput, pendingIntent,
- smartReplyController, entry);
+ getContext(), this, i, smartReplies, smartReplyController, entry);
addView(replyButton);
}
}
@@ -209,12 +208,14 @@
* Add smart actions to be shown next to smart replies. Only the actions that fit into the
* notification are shown.
*/
- public void addSmartActions(List<Notification.Action> smartActions) {
- int numSmartActions = smartActions.size();
+ public void addSmartActions(SmartActions smartActions,
+ SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
- Notification.Action action = smartActions.get(n);
+ Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
- Button actionButton = inflateActionButton(getContext(), this, action);
+ Button actionButton = inflateActionButton(
+ getContext(), this, n, smartActions, smartReplyController, entry);
addView(actionButton);
}
}
@@ -228,22 +229,25 @@
@VisibleForTesting
Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
- CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ SmartReplies smartReplies, SmartReplyController smartReplyController,
+ NotificationData.Entry entry) {
Button b = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_reply_button, root, false);
+ CharSequence choice = smartReplies.choices[replyIndex];
b.setText(choice);
OnDismissAction action = () -> {
- smartReplyController.smartReplySent(entry, replyIndex, b.getText());
+ smartReplyController.smartReplySent(
+ entry, replyIndex, b.getText(), smartReplies.fromAssistant);
Bundle results = new Bundle();
- results.putString(remoteInput.getResultKey(), choice.toString());
+ results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+ RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+ results);
RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
entry.setHasSentReply();
try {
- pendingIntent.send(context, 0, intent);
+ smartReplies.pendingIntent.send(context, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Unable to send smart reply", e);
}
@@ -268,47 +272,35 @@
}
@VisibleForTesting
- Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+ Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
+ SmartActions smartActions, SmartReplyController smartReplyController,
+ NotificationData.Entry entry) {
+ Notification.Action action = smartActions.actions.get(actionIndex);
Button button = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_action_button, root, false);
button.setText(action.title);
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));
+ getActivityStarter().startPendingIntentDismissingKeyguard(
+ action.actionIntent,
+ () -> smartReplyController.smartActionClicked(
+ entry, actionIndex, action, smartActions.fromAssistant)));
// 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);
- }
-
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(mContext, attrs);
@@ -342,18 +334,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.
@@ -407,7 +407,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.
@@ -416,7 +417,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;
@@ -435,6 +437,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++) {
@@ -605,9 +623,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;
@@ -702,6 +720,11 @@
return mActivityStarter;
}
+ private enum SmartButtonType {
+ REPLY,
+ ACTION
+ }
+
@VisibleForTesting
static class LayoutParams extends ViewGroup.LayoutParams {
@@ -727,6 +750,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);
@@ -741,4 +765,40 @@
return show;
}
}
+
+ /**
+ * Data class for smart replies.
+ */
+ public static class SmartReplies {
+ @NonNull
+ public final RemoteInput remoteInput;
+ @NonNull
+ public final PendingIntent pendingIntent;
+ @NonNull
+ public final CharSequence[] choices;
+ public final boolean fromAssistant;
+
+ public SmartReplies(CharSequence[] choices, RemoteInput remoteInput,
+ PendingIntent pendingIntent, boolean fromAssistant) {
+ this.choices = choices;
+ this.remoteInput = remoteInput;
+ this.pendingIntent = pendingIntent;
+ this.fromAssistant = fromAssistant;
+ }
+ }
+
+
+ /**
+ * Data class for smart actions.
+ */
+ public static class SmartActions {
+ @NonNull
+ public final List<Notification.Action> actions;
+ public final boolean fromAssistant;
+
+ public SmartActions(List<Notification.Action> actions, boolean fromAssistant) {
+ this.actions = actions;
+ this.fromAssistant = fromAssistant;
+ }
+ }
}
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/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/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 6ac4462..ec2319d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -16,9 +16,8 @@
package com.android.systemui.doze;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,8 +26,8 @@
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import org.junit.Before;
@@ -59,14 +58,14 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), anyLong());
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
// Make sure we're sending false when AoD is off
reset(mDozeParameters);
mDozeWallpaperState.transitionTo(DozeMachine.State.FINISH, DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
}
@Test
@@ -78,10 +77,12 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
}
@Test
@@ -93,24 +94,24 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L));
}
@Test
public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE,
DozeMachine.State.DOZE_REQUEST_PULSE);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
public void testTransitionTo_pulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
@@ -120,6 +121,7 @@
reset(mIWallpaperManager);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_PULSING,
DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
}
}
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 a26b1b5..c1c80ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,11 +19,9 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.ContentResolver;
@@ -34,10 +32,15 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.core.SliceQuery;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
import org.junit.Assert;
import org.junit.Before;
@@ -50,16 +53,9 @@
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
-import androidx.slice.Slice;
-import androidx.slice.SliceItem;
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.core.SliceQuery;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class KeyguardSliceProviderTest extends SysuiTestCase {
@Mock
@@ -164,7 +160,7 @@
}
@Override
- protected boolean isDndSuppressingNotifications() {
+ protected boolean isDndOn() {
return mIsZenMode;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 221cbe9..c28e74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -37,6 +37,7 @@
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
@@ -127,6 +128,8 @@
resources.addOverride(R.integer.config_warningTemperature, 55);
mPowerUI.start();
+ // Guarantees mHandler has processed all messages.
+ TestableLooper.get(this).processAllMessages();
verify(mMockWarnings).showHighTemperatureWarning();
}
@@ -139,6 +142,8 @@
resources.addOverride(R.integer.config_warningTemperature, 55);
mPowerUI.start();
+ // Guarantees mHandler has processed all messages.
+ TestableLooper.get(this).processAllMessages();
verify(mMockWarnings).showHighTemperatureWarning();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 48491d7..24bcca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -16,8 +16,12 @@
package com.android.systemui.privacy
+import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.Intent
import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
import android.support.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -34,9 +38,11 @@
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
-
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -44,10 +50,18 @@
@RunWithLooper
class PrivacyItemControllerTest : SysuiTestCase() {
+ companion object {
+ val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+ val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+ const val TAG = "PrivacyItemControllerTest"
+ }
+
@Mock
private lateinit var appOpsController: AppOpsController
@Mock
private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
@@ -57,15 +71,17 @@
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java)
+ appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+ mContext.addMockSystemService(UserManager::class.java, userManager)
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
privacyItemController = PrivacyItemController(mContext, callback)
}
+
@Test
fun testSetListeningTrue() {
privacyItemController.setListening(true)
@@ -80,6 +96,38 @@
privacyItemController.setListening(true)
privacyItemController.setListening(false)
verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
- any(AppOpsController.Callback:: class.java))
+ any(AppOpsController.Callback::class.java))
+ }
+
+ @Test
+ fun testRegisterReceiver_allUsers() {
+ val spiedContext = spy(mContext)
+ val itemController = PrivacyItemController(spiedContext, callback)
+
+ verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
+ eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
+ eq(null))
+ verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
+ }
+
+ @Test
+ fun testReceiver_ACTION_USER_FOREGROUND() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_USER_FOREGROUND))
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
+ verify(userManager).getProfiles(anyInt())
}
}
\ No newline at end of file
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/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index f63d236..26fa20d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -14,41 +14,76 @@
package com.android.systemui.qs.customize;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.systemui.Dependency;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
- @Mock private TileQueryHelper.TileStateListener mListener;
- @Mock private QSTileHost mQSTileHost;
+ private static final String CURRENT_TILES = "wifi,dnd,nfc";
+ private static final String ONLY_STOCK_TILES = "wifi,dnd";
+ private static final String WITH_OTHER_TILES = "wifi,dnd,other";
+ // Note no nfc in stock tiles
+ private static final String STOCK_TILES = "wifi,dnd,cell,battery";
+ private static final String ALL_TILES = "wifi,dnd,nfc,cell,battery";
+ private static final Set<String> FACTORY_TILES = new ArraySet<>();
+ static {
+ FACTORY_TILES.addAll(Arrays.asList(
+ new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
+ "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
+ "saver", "night", "nfc"}));
+ }
+
+ @Mock
+ private TileQueryHelper.TileStateListener mListener;
+ @Mock
+ private QSTileHost mQSTileHost;
+ @Mock
+ private PackageManager mPackageManager;
+
+ private QSTile.State mState;
private TestableLooper mBGLooper;
-
private TileQueryHelper mTileQueryHelper;
@Before
@@ -56,6 +91,23 @@
MockitoAnnotations.initMocks(this);
mBGLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+ mContext.setMockPackageManager(mPackageManager);
+
+ mState = new QSTile.State();
+ doAnswer(invocation -> {
+ String spec = (String) invocation.getArguments()[0];
+ if (FACTORY_TILES.contains(spec)) {
+ QSTile m = mock(QSTile.class);
+ when(m.isAvailable()).thenReturn(true);
+ when(m.getTileSpec()).thenReturn(spec);
+ when(m.getState()).thenReturn(mState);
+ return m;
+ } else {
+ return null;
+ }
+ }
+ ).when(mQSTileHost).createTile(anyString());
+
mTileQueryHelper = new TileQueryHelper(mContext, mListener);
}
@@ -98,4 +150,72 @@
assertTrue(mTileQueryHelper.isFinished());
}
+
+ @Test
+ public void testQueryTiles_correctTilesAndOrderOnlyStockTiles() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ ONLY_STOCK_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ String tiles = TextUtils.join(",", specs);
+ assertThat(tiles, is(equalTo(STOCK_TILES)));
+ }
+
+ @Test
+ public void testQueryTiles_correctTilesAndOrderOtherFactoryTiles() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ CURRENT_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ String tiles = TextUtils.join(",", specs);
+ assertThat(tiles, is(equalTo(ALL_TILES)));
+ }
+
+ @Test
+ public void testQueryTiles_otherTileNotIncluded() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ WITH_OTHER_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ assertFalse(specs.contains("other"));
+ }
}
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/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f49c5b4..9d0556f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -120,7 +120,7 @@
mTestHandler = Handler.createAsync(Looper.myLooper());
mSbn = createNewNotification(0 /* id */);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mAlertingNotificationManager = createAlertingNotificationManager();
}
@@ -171,7 +171,7 @@
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
StatusBarNotification sbn = createNewNotification(i);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- entry.row = mRow;
+ entry.setRow(mRow);
mAlertingNotificationManager.showNotification(entry);
}
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 d409e2b..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;
@@ -78,9 +77,9 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ 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 aca1f90..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);
@@ -227,11 +239,12 @@
null /* overrideGroupKey */,
System.currentTimeMillis());
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- entry.row = row;
+ entry.setRow(row);
entry.createIcons(mContext, sbn);
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 602e613..520a927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -45,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;
@@ -59,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;
@@ -73,11 +75,12 @@
@Mock private ShadeController mShadeController;
private NotificationViewHierarchyManager mViewHierarchyManager;
- private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);;
+ private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
@@ -95,7 +98,7 @@
private NotificationData.Entry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
- entry.row = row;
+ entry.setRow(row);
return entry;
}
@@ -108,9 +111,9 @@
NotificationData.Entry entry2 = createEntry();
// Set up the prior state to look like three top level notifications.
- mListContainer.addContainerView(entry0.row);
- mListContainer.addContainerView(entry1.row);
- mListContainer.addContainerView(entry2.row);
+ mListContainer.addContainerView(entry0.getRow());
+ mListContainer.addContainerView(entry1.getRow());
+ mListContainer.addContainerView(entry2.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
@@ -118,15 +121,15 @@
when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
- when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
- when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+ when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
+ when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
// Run updateNotifications - the view hierarchy should be reorganized.
mViewHierarchyManager.updateNotificationViews();
- verify(mListContainer).notifyGroupChildAdded(entry1.row);
- verify(mListContainer).notifyGroupChildAdded(entry2.row);
- assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+ verify(mListContainer).notifyGroupChildAdded(entry1.getRow());
+ verify(mListContainer).notifyGroupChildAdded(entry2.getRow());
+ assertTrue(Lists.newArrayList(entry0.getRow()).equals(mListContainer.mRows));
}
@Test
@@ -135,11 +138,11 @@
NotificationData.Entry entry0 = createEntry();
NotificationData.Entry entry1 = createEntry();
NotificationData.Entry entry2 = createEntry();
- entry0.row.addChildNotification(entry1.row);
- entry0.row.addChildNotification(entry2.row);
+ entry0.getRow().addChildNotification(entry1.getRow());
+ entry0.getRow().addChildNotification(entry2.getRow());
// Set up the prior state to look like one top level notification.
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
@@ -152,10 +155,12 @@
mViewHierarchyManager.updateNotificationViews();
verify(mListContainer).notifyGroupChildRemoved(
- entry1.row, entry0.row.getChildrenContainer());
+ entry1.getRow(), entry0.getRow().getChildrenContainer());
verify(mListContainer).notifyGroupChildRemoved(
- entry2.row, entry0.row.getChildrenContainer());
- assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+ entry2.getRow(), entry0.getRow().getChildrenContainer());
+ assertTrue(
+ Lists.newArrayList(entry0.getRow(), entry1.getRow(), entry2.getRow())
+ .equals(mListContainer.mRows));
}
@Test
@@ -163,10 +168,10 @@
// Tests two top level notifications becoming a suppressed summary and a child.
NotificationData.Entry entry0 = createEntry();
NotificationData.Entry entry1 = createEntry();
- entry0.row.addChildNotification(entry1.row);
+ entry0.getRow().addChildNotification(entry1.getRow());
// Set up the prior state to look like a top level notification.
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1));
@@ -179,23 +184,23 @@
mViewHierarchyManager.updateNotificationViews();
verify(mListContainer).notifyGroupChildRemoved(
- entry1.row, entry0.row.getChildrenContainer());
- assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
- assertEquals(View.GONE, entry0.row.getVisibility());
- assertEquals(View.VISIBLE, entry1.row.getVisibility());
+ entry1.getRow(), entry0.getRow().getChildrenContainer());
+ assertTrue(Lists.newArrayList(entry0.getRow(), entry1.getRow()).equals(mListContainer.mRows));
+ assertEquals(View.GONE, entry0.getRow().getVisibility());
+ assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
}
@Test
public void testUpdateNotificationViews_appOps() throws Exception {
NotificationData.Entry entry0 = createEntry();
- entry0.row = spy(entry0.row);
+ entry0.setRow(spy(entry0.getRow()));
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0));
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
mViewHierarchyManager.updateNotificationViews();
- verify(entry0.row, times(1)).showAppOpsIcons(any());
+ verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
}
private class FakeListContainer implements NotificationListContainer {
@@ -206,19 +211,19 @@
public void setChildTransferInProgress(boolean childTransferInProgress) {}
@Override
- public void changeViewPosition(View child, int newIndex) {
+ public void changeViewPosition(ExpandableView child, int newIndex) {
mRows.remove(child);
mRows.add(newIndex, child);
}
@Override
- public void notifyGroupChildAdded(View row) {}
+ public void notifyGroupChildAdded(ExpandableView row) {}
@Override
- public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {}
+ public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
@Override
- public void generateAddAnimation(View child, boolean fromMoreCard) {}
+ public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
@Override
public void generateChildOrderChangedEvent() {}
@@ -252,7 +257,7 @@
public void setMaxDisplayedNotifications(int maxNotifications) {}
@Override
- public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+ public void snapViewIfNeeded(Entry entry) {}
@Override
public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
@@ -271,10 +276,10 @@
}
@Override
- public void cleanUpViewState(View view) {}
+ public void cleanUpViewStateForEntry(Entry entry) { }
@Override
- public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+ public boolean isInVisibleLocation(Entry entry) {
return true;
}
@@ -286,5 +291,6 @@
public boolean hasPulsingNotifications() {
return false;
}
+
}
}
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 1d977d8..8d52ccd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -16,18 +16,15 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -38,7 +35,6 @@
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 org.junit.Before;
import org.junit.Test;
@@ -80,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")
@@ -93,7 +89,7 @@
@Test
public void testSendSmartReply_updatesRemoteInput() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
// Sending smart reply should make calls to NotificationEntryManager
// to update the notification with reply and spinner.
@@ -103,11 +99,21 @@
@Test
public void testSendSmartReply_logsToStatusBar() throws RemoteException {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
// Check we log the result to the status bar service.
verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
- TEST_CHOICE_INDEX);
+ TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
+ }
+
+
+ @Test
+ public void testSendSmartReply_logsToStatusBar_generatedByAssistant() throws RemoteException {
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
+
+ // Check we log the result to the status bar service.
+ verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
+ TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
}
@Test
@@ -121,14 +127,14 @@
@Test
public void testSendSmartReply_reportsSending() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
}
@Test
public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
mSmartReplyController.stopSending(mEntry);
assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
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/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 435ede4..5558393 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -62,6 +62,7 @@
@Before
public void setUp() throws Exception {
+ when(mStatusBarWindowView.getResources()).thenReturn(mContext.getResources());
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
mLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindowView,
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 b3b45eb..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);
@@ -121,9 +123,9 @@
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mNotificationData = new TestableNotificationData();
- Dependency.get(InitController.class).executePostInitTasks();
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
mRow = new NotificationTestHelper(getContext()).createRow();
+ Dependency.get(InitController.class).executePostInitTasks();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index d1fe5af..8706e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -262,14 +262,14 @@
NotificationData.Entry.class);
verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
NotificationData.Entry entry = entryCaptor.getValue();
- verify(mRemoteInputManager).bindRow(entry.row);
+ verify(mRemoteInputManager).bindRow(entry.getRow());
// Row content inflation:
verify(mCallback).onNotificationAdded(entry);
verify(mPresenter).updateNotificationViews();
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
- assertNotNull(entry.row);
+ assertNotNull(entry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@@ -294,7 +294,7 @@
verify(mPresenter).updateNotificationViews();
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
- assertNotNull(mEntry.row);
+ assertNotNull(mEntry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@@ -303,7 +303,7 @@
public void testRemoveNotification() throws Exception {
com.android.systemui.util.Assert.isNotMainThread();
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -313,7 +313,7 @@
verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
verify(mForegroundServiceController).removeNotification(mSbn);
- verify(mListContainer).cleanUpViewState(mRow);
+ verify(mListContainer).cleanUpViewStateForEntry(mEntry);
verify(mPresenter).updateNotificationViews();
verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
verify(mRow).setRemoved();
@@ -332,7 +332,7 @@
extenders.clear();
extenders.add(extender);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -347,7 +347,7 @@
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
.thenReturn(mEntry.key);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.updateNotificationsForAppOp(
@@ -372,7 +372,7 @@
@Test
public void testAddNotificationExistingAppOps() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
ArraySet<Integer> expected = new ArraySet<>();
expected.add(3);
@@ -395,7 +395,7 @@
@Test
public void testAdd_noExistingAppOps() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
when(mForegroundServiceController.getStandardLayoutKey(
mEntry.notification.getUserId(),
@@ -409,7 +409,7 @@
@Test
public void testAdd_existingAppOpsNotForegroundNoti() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
ArraySet<Integer> ops = new ArraySet<>();
ops.add(3);
@@ -431,7 +431,7 @@
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntry.setInflationTask(mAsyncInflationTask);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -447,7 +447,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, null);
@@ -461,7 +461,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = null;
+ mEntry.setRow(null);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -476,7 +476,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = null;
+ mEntry.setRow(null);
mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
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/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index ffb1c2d..f190979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -47,7 +47,7 @@
public void setUp() {
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
}
@Test
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 1c7a8e8..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;
@@ -89,7 +89,7 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mLogger = new TestableNotificationLogger(mBarService);
mLogger.setUpWithContainer(mListContainer);
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/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index a6725b8..f0fa788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,11 +16,9 @@
package com.android.systemui.statusbar.notification.row;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
@@ -106,7 +104,8 @@
mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
- // Smart replies
+ // Smart replies and actions
+ when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
mEntry = new NotificationData.Entry(mStatusBarNotification);
when(mSmartReplyConstants.isEnabled()).thenReturn(true);
@@ -169,8 +168,10 @@
private void setupAppGeneratedReplies(
CharSequence[] smartReplyTitles,
Notification.Action freeFormRemoteInputAction) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
Notification.Action action =
- new Notification.Action.Builder(null, "Test Action", null).build();
+ new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
Pair<RemoteInput, Notification.Action> remoteInputActionPair =
Pair.create(mRemoteInput, action);
@@ -190,7 +191,7 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertFalse(repliesAndActions.smartRepliesExist());
+ assertThat(repliesAndActions.smartReplies).isNull();
}
@Test
@@ -202,7 +203,9 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions).isNull();
}
@Test
@@ -218,8 +221,10 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
- assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
}
@Test
@@ -236,8 +241,9 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
- assertThat(repliesAndActions.smartActions, is(empty()));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
+ assertThat(repliesAndActions.smartActions).isNull();
}
@Test
@@ -254,8 +260,8 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(null));
- assertThat(repliesAndActions.smartActions, is(empty()));
+ assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(repliesAndActions.smartActions).isNull();
}
@Test
@@ -269,8 +275,10 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(null));
- assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+ assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(repliesAndActions.smartActions.actions)
+ .isEqualTo(mEntry.systemGeneratedSmartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
}
@Test
@@ -295,8 +303,27 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
- assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // actions.
+ setupAppGeneratedReplies(null);
+
+ when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
+
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(repliesAndActions.smartReplies).isNull();
}
private Notification.Action.Builder createActionBuilder(String actionTitle) {
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 3d2ea70..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
@@ -20,8 +20,7 @@
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -60,12 +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.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;
@@ -81,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";
@@ -94,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;
@@ -103,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());
@@ -112,6 +112,7 @@
mGutsManager = new NotificationGutsManager(mContext);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
+ mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -177,10 +178,17 @@
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
ExpandableNotificationRow row = spy(realRow);
+
when(row.getWindowToken()).thenReturn(new Binder());
when(row.getGuts()).thenReturn(guts);
doNothing().when(row).inflateGuts();
+ NotificationData.Entry realEntry = realRow.getEntry();
+ NotificationData.Entry entry = spy(realEntry);
+
+ when(entry.getRow()).thenReturn(row);
+ when(entry.getGuts()).thenReturn(guts);
+
mGutsManager.openGuts(row, 0, 0, menuItem);
mTestableLooper.processAllMessages();
verify(guts).openControls(
@@ -190,13 +198,19 @@
anyBoolean(),
any(Runnable.class));
+ // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+ verify(row).setGutsView(any());
+
row.onDensityOrFontScaleChanged();
- mGutsManager.onDensityOrFontScaleChanged(row);
+ mGutsManager.onDensityOrFontScaleChanged(entry);
+
mTestableLooper.processAllMessages();
mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+
+ // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
verify(row, times(2)).setGutsView(any());
}
@@ -206,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());
}
@@ -216,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());
}
@@ -227,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());
}
@@ -237,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());
}
@@ -249,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());
}
@@ -260,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());
}
@@ -271,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());
}
@@ -284,8 +305,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -301,9 +321,7 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -315,8 +333,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -332,41 +349,7 @@
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
- }
-
- @Test
- public void testInitializeNotificationInfoView_noisy() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- row.setBlockingHelperShowing(true);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
- row.getEntry().noisy = true;
- when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
-
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- anyInt(),
- eq(statusBarNotification),
- any(NotificationInfo.CheckSaveListener.class),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- eq(false),
- eq(false),
- eq(true) /* isForBlockingHelper */,
- eq(true) /* isUserSentimentNegative */,
- eq(true) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -379,8 +362,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -396,9 +378,7 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(IMPORTANCE_DEFAULT),
- eq(NotificationInfo.ACTION_NONE));
+ eq(IMPORTANCE_DEFAULT));
}
@Test
@@ -411,8 +391,7 @@
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -428,9 +407,7 @@
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -442,8 +419,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_BLOCK);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -459,9 +435,7 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_BLOCK));
+ eq(0));
}
@Test
@@ -470,7 +444,7 @@
ExpandableNotificationRow row = spy(createTestNotificationRow());
doReturn(guts).when(row).getGuts();
NotificationData.Entry entry = row.getEntry();
- entry.row = row;
+ entry.setRow(row);
mGutsManager.setExposedGuts(guts);
assertTrue(mGutsManager.shouldExtendLifetime(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 1cc1c63..d28f017 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -50,12 +50,9 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.os.UserHandle;
@@ -86,8 +83,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
@SmallTest
@@ -134,7 +129,7 @@
.thenReturn(packageInfo);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = TEST_UID; // non-zero
- when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
applicationInfo);
final PackageInfo systemPackageInfo = new PackageInfo();
systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -187,7 +182,7 @@
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -200,16 +195,47 @@
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@Test
+ public void testBindNotification_noDelegate() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(GONE, nameView.getVisibility());
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(GONE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_delegate() throws Exception {
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = 7; // non-zero
+ when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+ applicationInfo);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(VISIBLE, nameView.getVisibility());
+ assertTrue(nameView.getText().toString().contains("Other"));
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(VISIBLE, dividerView.getVisibility());
+ }
+
+ @Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -226,7 +252,7 @@
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -238,7 +264,7 @@
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -247,7 +273,7 @@
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -260,7 +286,7 @@
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -269,7 +295,7 @@
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -278,62 +304,104 @@
public void testBindNotification_BlockButton() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final View block = mNotificationInfo.findViewById(R.id.block);
- final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- final View minimize = mNotificationInfo.findViewById(R.id.minimize);
+ IMPORTANCE_DEFAULT);
+ final View block = mNotificationInfo.findViewById(R.id.int_block);
+ final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize);
assertEquals(VISIBLE, block.getVisibility());
- assertEquals(GONE, toggleSilent.getVisibility());
assertEquals(GONE, minimize.getVisibility());
}
@Test
- public void testBindNotification_SilenceButton() throws Exception {
+ public void testBindNotification_BlockButton_BlockHelper() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT);
+ final View block = mNotificationInfo.findViewById(R.id.block);
+ final View interruptivenessSettings = mNotificationInfo.findViewById(
+ R.id.interruptiveness_settings);
+ assertEquals(VISIBLE, block.getVisibility());
+ assertEquals(GONE, interruptivenessSettings.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_SilenceButton_CurrentlyAlerting() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_DEFAULT);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_silent), silent.getText());
}
@Test
- public void testBindNotification_UnSilenceButton() throws Exception {
+ public void testBindNotification_SilenceButton_CurrentlySilent() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_LOW);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_stay_silent),
+ silent.getText());
}
@Test
- public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+ public void testBindNotification_AlertButton_CurrentlySilent() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_LOW);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_alert), alert.getText());
}
@Test
- public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified()
- throws Exception {
+ public void testBindNotification_UnSilenceButton_currentlyAlerting() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, alert.getVisibility());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText());
+ }
+
+ @Test
+ public void testBindNotification_ChannelImportanceUnspecified_NotifAlerting() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_DEFAULT);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, silent.getVisibility());
+ assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_silent), silent.getText());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText());
+ }
+
+ @Test
+ public void testBindNotification_ChannelImportanceUnspecified_NotifSilent() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_LOW);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, silent.getVisibility());
+ assertEquals(VISIBLE, alert.getVisibility());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_stay_silent), silent.getText());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_alert), alert.getText());
}
@Test
@@ -341,10 +409,13 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final View block = mNotificationInfo.findViewById(R.id.block);
+ final View interruptivenessSettings = mNotificationInfo.findViewById(
+ R.id.interruptiveness_settings);
final View minimize = mNotificationInfo.findViewById(R.id.minimize);
assertEquals(GONE, block.getVisibility());
+ assertEquals(GONE, interruptivenessSettings.getVisibility());
assertEquals(VISIBLE, minimize.getVisibility());
}
@@ -356,7 +427,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -368,7 +439,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -380,7 +451,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, false, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -389,11 +460,11 @@
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -402,7 +473,7 @@
public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
}
@@ -411,7 +482,7 @@
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true, IMPORTANCE_DEFAULT);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
}
@@ -424,7 +495,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -437,7 +508,7 @@
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ null, true, true, IMPORTANCE_DEFAULT);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -448,7 +519,7 @@
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ null, true, true, IMPORTANCE_DEFAULT);
final TextView blockView = mNotificationInfo.findViewById(R.id.block);
assertEquals(GONE, blockView.getVisibility());
}
@@ -457,7 +528,7 @@
public void testbindNotification_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true, IMPORTANCE_DEFAULT);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -467,7 +538,7 @@
public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -478,7 +549,7 @@
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -489,9 +560,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -503,7 +574,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
mTestableLooper.processAllMessages();
@@ -517,9 +588,9 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -531,9 +602,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -545,7 +616,7 @@
int originalImportance = mNotificationChannel.getImportance();
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -560,7 +631,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
@@ -578,10 +649,10 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
- true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ );
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -600,10 +671,10 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */,
- true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ );
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -622,8 +693,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -651,8 +721,7 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -680,8 +749,8 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true, true /* isUserSentimentNegative */, false /* isNoisy */,
- IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true /* isUserSentimentNegative */, /* isNoisy */
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -700,8 +769,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -724,7 +792,7 @@
true /* isForBlockingHelper */,
true,
false /* isUserSentimentNegative */,
- false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -739,7 +807,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -753,9 +821,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -786,7 +854,7 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true /* isUserSentimentNegative */,
- false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -808,7 +876,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -823,7 +891,7 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -844,7 +912,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
@@ -862,9 +930,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.findViewById(R.id.undo).performClick();
waitForStopButton();
@@ -883,8 +951,8 @@
public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -906,9 +974,9 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -927,9 +995,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -949,9 +1017,9 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -971,9 +1039,9 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_LOW);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -988,11 +1056,11 @@
}
@Test
- public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
+ public void testCloseControlsDoesNotUpdateMinIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1007,10 +1075,10 @@
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(false, false);
@@ -1025,9 +1093,9 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
- }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, null, true, true, IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
ensureNoUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -1043,10 +1111,10 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, false, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ }, null, null, true, false, IMPORTANCE_DEFAULT
+ );
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
@@ -1060,124 +1128,6 @@
}
@Test
- public void testDisplaySettingsLink() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
- (View v, Intent intent) -> {
- latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.VISIBLE, settingsLink.getVisibility());
- settingsLink.performClick();
- assertEquals(0, latch.getCount());
- }
-
- @Test
- public void testDisplaySettingsLink_multipleChannels() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
- (View v, Intent intent) -> {
- latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.VISIBLE, settingsLink.getVisibility());
- settingsLink.performClick();
- assertEquals(0, latch.getCount());
- }
-
- @Test
- public void testNoSettingsLink_noHandlingActivity() throws Exception {
- final String settingsText = "work chats";
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
- null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(GONE, settingsLink.getVisibility());
- }
-
- @Test
- public void testNoSettingsLink_noLinkText() throws Exception {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(GONE, settingsLink.getVisibility());
- }
-
- @Test
- public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(GONE, settingsLink.getVisibility());
- }
-
-
- @Test
public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
assertFalse(mNotificationInfo.willBeRemoved());
}
@@ -1188,7 +1138,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1200,10 +1150,10 @@
public void testUndoText_block() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
assertTrue(confirmationText.getText().toString().contains("won't see"));
@@ -1213,10 +1163,10 @@
public void testUndoText_silence() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
assertEquals(mContext.getString(R.string.notification_channel_silenced),
@@ -1227,10 +1177,10 @@
public void testUndoText_unsilence() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
assertEquals(mContext.getString(R.string.notification_channel_unsilenced),
@@ -1241,10 +1191,10 @@
public void testNoHeaderOnConfirmation() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility());
}
@@ -1253,69 +1203,13 @@
public void testHeaderOnUndo() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.findViewById(R.id.undo).performClick();
waitForStopButton();
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
}
-
- @Test
- public void testBindNotificationWithInitialBlockAction() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK);
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true, false);
-
- mTestableLooper.processAllMessages();
- ArgumentCaptor<NotificationChannel> updated =
- ArgumentCaptor.forClass(NotificationChannel.class);
- verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
- anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
- }
-
- @Test
- public void testBindNotificationWithInitialSilenceAction() throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT);
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true, false);
-
- mTestableLooper.processAllMessages();
- ArgumentCaptor<NotificationChannel> updated =
- ArgumentCaptor.forClass(NotificationChannel.class);
- verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
- anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
- }
-
- @Test
- public void testBindNotificationWithInitialUnSilenceAction() throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT);
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true, false);
-
- mTestableLooper.processAllMessages();
- ArgumentCaptor<NotificationChannel> updated =
- ArgumentCaptor.forClass(NotificationChannel.class);
- verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
- anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
- }
}
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 a4a111a..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,12 +25,13 @@
import android.support.test.filters.SmallTest;
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.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import org.junit.Assert;
import org.junit.Before;
@@ -41,17 +42,18 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
public class NotificationRoundnessManagerTest extends SysuiTestCase {
private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
- private HashSet<View> mAnimatedChildren = new HashSet<>();
+ private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
private Runnable mRoundnessCallback = mock(Runnable.class);
private ExpandableNotificationRow mFirst;
private ExpandableNotificationRow mSecond;
@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/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 74ce5f6..e65e806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -325,7 +325,9 @@
// add notification
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- when(row.isClearable()).thenReturn(true);
+ NotificationData.Entry entry = mock(NotificationData.Entry.class);
+ when(row.getEntry()).thenReturn(entry);
+ when(entry.isClearable()).thenReturn(true);
mStackScroller.addContainerView(row);
mStackScroller.onUpdateRowStates();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 93d8aad..d99e46d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -40,7 +40,7 @@
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper()
@SmallTest
public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
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 10b0d83..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);
@@ -81,12 +82,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
}
@@ -95,12 +96,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertTrue(mHeadsUpAppearanceController.isShown());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertFalse(mHeadsUpAppearanceController.isShown());
}
@@ -109,12 +110,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
}
@@ -125,12 +126,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1070795..44deb10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -97,7 +97,7 @@
@Test
public void testCanRemoveImmediately_notTopEntry() {
NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
- laterEntry.row = mRow;
+ laterEntry.setRow(mRow);
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
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 f8ad298..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,14 +30,13 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final int SCREEN_HEIGHT = 2000;
private static final int EMPTY_MARGIN = 0;
private static final int EMPTY_HEIGHT = 0;
private static final boolean SECURE_LOCKED = false;
- private static final boolean PULSING_NO = false;
private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
@@ -48,6 +47,7 @@
private float mPanelExpansion;
private int mKeyguardStatusHeight;
private float mDark;
+ private boolean mPulsing;
@Before
public void setUp() {
@@ -171,6 +171,156 @@
assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
}
+ @Test
+ public void notifPositionMiddleOfScreenOnAOD() {
+ // GIVEN on AOD and both stack scroll and clock have 0 height
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
+ // GIVEN on AOD and clock has a nonzero height
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = 100;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionWithLargeClockOnAOD() {
+ // GIVEN on AOD and clock has a nonzero height
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = SCREEN_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is, unfortunately, the entire screen.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(SCREEN_HEIGHT);
+ }
+
+ @Test
+ public void notifPositionWhilePulsingOnAOD() {
+ // GIVEN on AOD and pulsing
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ mPulsing = true;
+ mClockPositionAlgorithm.setPulsingPadding(200);
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding doesn't adjust for pulsing.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionMiddleOfScreenOnLockScreen() {
+ // GIVEN on lock screen and both stack scroll and clock have 0 height
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionAdjustsForStackHeightOnLockScreen() {
+ // GIVEN on lock screen and stack scroller has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = 500;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for the expanded notif stack.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(750);
+ }
+
+ @Test
+ public void notifPositionAdjustsForClockHeightOnLockScreen() {
+ // GIVEN on lock screen and stack scroller has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = 200;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for both clock and notif stack.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
+ // GIVEN on lock screen and stack scroller has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = 500;
+ mKeyguardStatusHeight = 200;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for both clock and notif stack.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(810);
+ }
+
+ @Test
+ public void notifPositionWithLargeClockOnLockScreen() {
+ // GIVEN on lock screen and clock has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = SCREEN_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionWithFullDragOnLockScreen() {
+ // GIVEN the lock screen is dragged up
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ mPanelExpansion = 0.f;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding is zero.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+ }
+
+ @Test
+ public void notifPositionWithLargeClockFullDragOnLockScreen() {
+ // GIVEN the lock screen is dragged up and a full screen clock
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = SCREEN_HEIGHT;
+ mPanelExpansion = 0.f;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding is zero.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+ }
+
+ @Test
+ public void notifPositionWhilePulsingOnLockScreen() {
+ // GIVEN on lock screen and pulsing
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ mPulsing = true;
+ mClockPositionAlgorithm.setPulsingPadding(200);
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for pulsing.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200);
+ }
+
private void givenAOD() {
mPanelExpansion = 1.f;
mDark = 1.f;
@@ -184,7 +334,7 @@
private void positionClock() {
mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
- PULSING_NO, ZERO_DRAG);
+ mPulsing, ZERO_DRAG);
mClockPositionAlgorithm.run(mClockPosition);
}
}
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/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 96c57f2..c3bc511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -149,7 +149,8 @@
Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
Entry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -166,12 +167,14 @@
Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
Entry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(true);
mGroupAlertTransferHelper.onInflationFinished(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
@@ -185,7 +188,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
NotificationData.Entry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
@@ -199,10 +203,12 @@
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(true);
mGroupAlertTransferHelper.onInflationFinished(childEntry);
- verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+ verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
+ .getContentFlag());
assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
}
@@ -212,7 +218,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -229,7 +236,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -251,7 +259,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 1483ae5..b0bd1fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -100,7 +100,7 @@
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
- assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
}
@Test
@@ -143,9 +143,8 @@
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry.row,
- mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
@Test
@@ -161,8 +160,7 @@
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry.row,
- mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 01f44fd4..7ad68eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -81,7 +81,7 @@
0 /* postTime */);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- entry.row = row;
+ entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
when(row.getStatusBarNotification()).thenReturn(sbn);
when(row.isInflationFlagSet(anyInt())).thenReturn(true);
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 4177cd1..cdaa242 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;
@@ -39,11 +38,6 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
-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.res.Resources;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -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,7 +58,7 @@
/** atest QuickStepControllerTest */
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
@SmallTest
public class QuickStepControllerTest extends SysuiTestCase {
private QuickStepController mController;
@@ -78,6 +77,7 @@
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,6 +106,18 @@
}
@Test
+ public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
+ doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
+ mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
+ null /* swipeLeftAction */, null /* swipeRightAction */);
+
+ 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);
@@ -395,6 +407,7 @@
verify(mProxy, times(1)).onQuickScrubStart();
verify(mProxyService, times(1)).notifyQuickScrubStarted();
verify(mNavigationBarView, times(1)).updateSlippery();
+ verify(mProxy, never()).onMotionEvent(moveEvent1);
// Move again for scrub
MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, 200, y);
@@ -402,6 +415,7 @@
assertEquals(action, mController.getCurrentAction());
verify(action, times(1)).onGestureMove(200, y);
verify(mProxy, times(1)).onQuickScrubProgress(1f / 2);
+ verify(mProxy, never()).onMotionEvent(moveEvent2);
// Action up
MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
@@ -409,6 +423,7 @@
assertNull(mController.getCurrentAction());
verify(action, times(1)).onGestureEnd();
verify(mProxy, times(1)).onQuickScrubEnd();
+ verify(mProxy, never()).onMotionEvent(upEvent);
}
@Test
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 2104721..27123e4 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
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.StatusBarManager;
@@ -63,11 +64,13 @@
mContext.putComponent(CommandQueue.class, mCommandQueue);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
+ StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
+ when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
mStatusBar = new StatusBarNotificationPresenter(mContext,
mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
- mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class),
+ statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
- mock(ActivityLaunchAnimator.Callback.class));
+ mock(ActivityLaunchAnimator.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 9e659c8..506fa97 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
@@ -62,6 +62,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -181,7 +182,16 @@
public void testSendSmartReply_controllerCalled() {
setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
- verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
+ verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+ false /* generatedByAsssitant */);
+ }
+
+ @Test
+ public void testSendSmartReply_controllerCalled_generatedByAssistant() {
+ setSmartReplies(TEST_CHOICES, true);
+ mView.getChildAt(2).performClick();
+ verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+ true /* generatedByAsssitant */);
}
@Test
@@ -392,11 +402,17 @@
}
private void setSmartReplies(CharSequence[] choices) {
+ setSmartReplies(choices, false);
+ }
+
+ private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
+ SmartReplyView.SmartReplies smartReplies =
+ new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
mView.resetSmartSuggestions(mContainer);
- mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+ mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
}
private Notification.Action createAction(String actionTitle) {
@@ -415,12 +431,18 @@
private void setSmartActions(String[] actionTitles) {
mView.resetSmartSuggestions(mContainer);
- mView.addSmartActions(createActions(actionTitles));
+ mView.addSmartActions(
+ new SmartReplyView.SmartActions(createActions(actionTitles), false),
+ mLogger,
+ mEntry);
}
private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
setSmartReplies(choices);
- mView.addSmartActions(createActions(actionTitles));
+ mView.addSmartActions(
+ new SmartReplyView.SmartActions(createActions(actionTitles), false),
+ mLogger,
+ mEntry);
}
private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -453,9 +475,11 @@
// Add smart replies
Button previous = null;
+ SmartReplyView.SmartReplies smartReplies =
+ new SmartReplyView.SmartReplies(choices, null, null, false);
for (int i = 0; i < choices.length; ++i) {
- Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
- null, null, null, null);
+ Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies,
+ null, null);
current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
current.getPaddingBottom());
if (previous != null) {
@@ -536,7 +560,7 @@
mView.getChildAt(2).performClick();
- verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+ verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
}
@Test
@@ -721,7 +745,9 @@
}
private Button inflateActionButton(Notification.Action action) {
- return mView.inflateActionButton(getContext(), mView, action);
+ return mView.inflateActionButton(getContext(), mView, 0,
+ new SmartReplyView.SmartActions(Collections.singletonList(action), false),
+ mLogger, mEntry);
}
@Test
@@ -775,4 +801,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/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/AccentColorBlackOverlay/Android.mk
index e642a68..d316fbd 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := AccentColorBlack
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := AccentColorBlackOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
index a1bd582..3b99648 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
@@ -16,12 +16,10 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
- android:priority="1"/>
+ 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/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <application android:label="@string/accent_color_black_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
similarity index 75%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
index 31d1339..5648f91 100644
--- a/core/java/android/service/intelligence/SnapshotData.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 SnapshotData;
+-->
+<resources>
+ <color name="accent_device_default_light">#202020</color>
+ <color name="accent_device_default_dark">#FFFFFF</color>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
similarity index 81%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
index 5ca9d15..baf09b1 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- 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/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/AccentColorGreenOverlay/Android.mk
index e642a68..afc4287 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := AccentColorGreen
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := AccentColorGreenOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
index a1bd582..609d5be 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
@@ -16,12 +16,10 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
- android:priority="1"/>
+ 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/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <application android:label="@string/accent_color_green_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
similarity index 75%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
index 31d1339..089f08c 100644
--- a/core/java/android/service/intelligence/SnapshotData.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 SnapshotData;
+-->
+<resources>
+ <color name="accent_device_default_light">#1B873B</color>
+ <color name="accent_device_default_dark">#84C188</color>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
similarity index 81%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
index 5ca9d15..4de344c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- 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/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/AccentColorPurpleOverlay/Android.mk
index e642a68..3366169 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := AccentColorPurple
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := AccentColorPurpleOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
index a1bd582..497a358 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
@@ -16,12 +16,10 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
- android:priority="1"/>
+ 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/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <application android:label="@string/accent_color_purple_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/core/java/android/service/intelligence/SnapshotData.aidl b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
similarity index 75%
copy from core/java/android/service/intelligence/SnapshotData.aidl
copy to packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
index 31d1339..7e34bac 100644
--- a/core/java/android/service/intelligence/SnapshotData.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 SnapshotData;
+-->
+<resources>
+ <color name="accent_device_default_light">#725AFF</color>
+ <color name="accent_device_default_dark">#B5A9FC</color>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
similarity index 81%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
index 5ca9d15..d1eb95a 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- 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/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
index e642a68..ecad420 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarDefault
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarDefaultOverlay
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
similarity index 92%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
index a1bd582..1639fc5 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.default"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_default"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values-en-rXC/strings.xml
similarity index 65%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
rename to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values-en-rXC/strings.xml
index 6a1ce41..c5ed415 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
similarity index 70%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
index 4c3571a..d8b69cd 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
@@ -17,12 +17,8 @@
*/
-->
<resources>
- <!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
- <!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">48dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">48dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
index 5ca9d15..c933290 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Default Navigation Bar Experiment (48dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
index b4b2b16..b4cc34f 100644
--- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
@@ -16,7 +16,7 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.floating"
+ package="com.android.internal.experiment.navbar.type.floating"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
index 6a58453..30bca3c 100644
--- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
@@ -21,8 +21,4 @@
<dimen name="navigation_bar_height">0dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
<dimen name="navigation_bar_width">0dp</dimen>
- <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_height">48dp</dimen>
- <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_width">48dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
index e642a68..58cf134 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim24
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay24
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
similarity index 93%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
index a1bd582..aee543a 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.slim24"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_slim24"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
index 6a1ce41..40d9fbc 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/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">"Úzky navigačný panel (experiment)"</string>
+ <string name="experiment_navigationbar_overlay" msgid="9207872199884142345">"Slim Navigation Bar Experiment (24dp)"</string>
</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
index 4c3571a..58c653d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">24dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
similarity index 95%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
index 5ca9d15..670bc55 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (24dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
index e642a68..7ebbb74 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim32
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay32
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
similarity index 93%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
index a1bd582..10cf6a1 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.slim32"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_slim32"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values-en-rXC/strings.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values-en-rXC/strings.xml
index 6a1ce41..ca8c4fa 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
index 4c3571a..00dd8fe 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
+ <dimen name="navigation_bar_height">32dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
+ <dimen name="navigation_bar_width">32dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">32dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">32dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
similarity index 95%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
index 5ca9d15..b48661c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (32dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
index e642a68..28354e3 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim40
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay40
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
similarity index 93%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
index a1bd582..ce8133f 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.slim40"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_slim40"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values-en-rXC/strings.xml
similarity index 65%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values-en-rXC/strings.xml
index 6a1ce41..9209c2b 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
similarity index 82%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
rename to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
index 4c3571a..4e65f33 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
+ <dimen name="navigation_bar_height">40dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
+ <dimen name="navigation_bar_width">40dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">40dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">40dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
similarity index 95%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
index 5ca9d15..8fe3a5c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (40dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml
deleted file mode 100644
index 21a0003..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml
deleted file mode 100644
index 6a7d644..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml
deleted file mode 100644
index 2cebf40..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml
deleted file mode 100644
index 8cce570..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/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/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml
deleted file mode 100644
index 266c051..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index dc57a3f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml
deleted file mode 100644
index 8b53bcd..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml
deleted file mode 100644
index 4bf000e..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml
deleted file mode 100644
index c0ab3b1..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/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/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml
deleted file mode 100644
index 9814209..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml
deleted file mode 100644
index 4a9b598..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml
deleted file mode 100644
index d923a3c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml
deleted file mode 100644
index 12bb2f2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml
deleted file mode 100644
index 960a7d9..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml
deleted file mode 100644
index 119b7e6..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml
deleted file mode 100644
index 5ebf403..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml
deleted file mode 100644
index 79884ce..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/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/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml
deleted file mode 100644
index 00b0444..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml
deleted file mode 100644
index 58bdaa2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml
deleted file mode 100644
index 4c5023c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml
deleted file mode 100644
index fc16eeb..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml
deleted file mode 100644
index e4246d7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml
deleted file mode 100644
index 9385a47..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml
deleted file mode 100644
index d22848b..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml
deleted file mode 100644
index cfa1db2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml
deleted file mode 100644
index d2194ce..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml
deleted file mode 100644
index 96418ae..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/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/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml
deleted file mode 100644
index d51cee3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml
deleted file mode 100644
index 410c58e..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml
deleted file mode 100644
index d7eafed..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml
deleted file mode 100644
index b0200ca..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml
deleted file mode 100644
index 3e0bded..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml
deleted file mode 100644
index 03ccaf3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml
deleted file mode 100644
index f7c5d253..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml
deleted file mode 100644
index 0d0ec2c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml
deleted file mode 100644
index a3d6874..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml
deleted file mode 100644
index ffddf3b..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml
deleted file mode 100644
index f34ac08..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml
deleted file mode 100644
index 114a782..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml
deleted file mode 100644
index ccdddea..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/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/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml
deleted file mode 100644
index fca02c3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml
deleted file mode 100644
index 449de4f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml
deleted file mode 100644
index 6ec48ca..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml
deleted file mode 100644
index 1df54aa..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml
deleted file mode 100644
index 5c6c565..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml
deleted file mode 100644
index 3517813..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml
deleted file mode 100644
index b65afe3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/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/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml
deleted file mode 100644
index a2282c4..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml
deleted file mode 100644
index c714370..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml
deleted file mode 100644
index 68f831e..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml
deleted file mode 100644
index 84db279..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml
deleted file mode 100644
index e1ff863..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml
deleted file mode 100644
index 6022b7f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/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/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml
deleted file mode 100644
index 01190bc..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml
deleted file mode 100644
index 1db9783..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/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/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml
deleted file mode 100644
index a782f46..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/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/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml
deleted file mode 100644
index 1742aad..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 22194b7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml
deleted file mode 100644
index f6c0309..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml
deleted file mode 100644
index 22194b7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml
deleted file mode 100644
index e1655f2..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml
deleted file mode 100644
index cac66dc..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml
deleted file mode 100644
index a1abb64..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/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/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml
deleted file mode 100644
index beab7b6..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml
deleted file mode 100644
index b7a28d5..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml
deleted file mode 100644
index 048f649..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml
deleted file mode 100644
index b94438f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml
deleted file mode 100644
index 3a5a73c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml
deleted file mode 100644
index 9e95c38..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/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/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml
deleted file mode 100644
index d273ab7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/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/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml
deleted file mode 100644
index 945297b..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml
deleted file mode 100644
index 0c8087c..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml
deleted file mode 100644
index a3ca754..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml
deleted file mode 100644
index 656c4a9..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml
deleted file mode 100644
index bcd6bc3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml
deleted file mode 100644
index 0d40981..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml
deleted file mode 100644
index dad56b4..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml
deleted file mode 100644
index b2602e0..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml
deleted file mode 100644
index d5259d3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/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/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 6586a57..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml
deleted file mode 100644
index 12d04d3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/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/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index e642a68..a734a6b 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := IconShapeRoundedRect
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := IconShapeRoundedRectOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
similarity index 72%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
index a1bd582..39c082b 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
@@ -16,12 +16,12 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
+ package="com.android.theme.icon.roundedrect"
+ android:versionCode="1"
+ android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="android.theme.customization.adaptive_icon_shape"
android:priority="1"/>
- <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <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/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
index 5ca9d15..dc5c196 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- Rounded corner rectangle overlay -->
+ <string name="icon_shape_roundedrect_overlay" translatable="false">RoundedRectangle Icons</string>
+
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/IconShapeSquareOverlay/Android.mk
index e642a68..217da9f 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := IconShapeSquare
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := IconShapeSquareOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
similarity index 72%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
index a1bd582..235fdeb 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
@@ -16,12 +16,12 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
+ package="com.android.theme.icon.square"
+ android:versionCode="1"
+ android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="android.theme.customization.adaptive_icon_shape"
android:priority="1"/>
- <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <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/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
similarity index 84%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
index 5ca9d15..4fd39ff26 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- Square icon overlay -->
+ <string name="icon_shape_square_overlay" translatable="false">Square Icons</string>
+
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/IconShapeSquircleOverlay/Android.mk
index e642a68..fd3bfa0 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := IconShapeSquircle
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := IconShapeSquircleOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
similarity index 72%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
index a1bd582..ca618e4 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
@@ -16,12 +16,12 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
+ package="com.android.theme.icon.squircle"
+ android:versionCode="1"
+ android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="android.theme.customization.adaptive_icon_shape"
android:priority="1"/>
- <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <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/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
similarity index 84%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
index 5ca9d15..b7c001c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- Squircle icon shape overlay -->
+ <string name="icon_shape_squircle_overlay" translatable="false">Square Icons</string>
+
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
similarity index 88%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
rename to packages/overlays/IconShapeTeardropOverlay/Android.mk
index e642a68..ea43423 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := IconShapeTeardrop
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := IconShapeTeardropOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
similarity index 72%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
index a1bd582..b7d5ecb 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
@@ -16,12 +16,12 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
+ package="com.android.theme.icon.teardrop"
+ android:versionCode="1"
+ android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="android.theme.customization.adaptive_icon_shape"
android:priority="1"/>
- <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <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/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
similarity index 84%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
index 5ca9d15..d946ee8 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- Teardrop icon overlay -->
+ <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop Icons</string>
+
+</resources>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3e07d12..89220d5 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6604,6 +6604,11 @@
// OS: Q
NOTIFICATION_DIRECT_REPLY_ACTION = 1590;
+ // OPEN: Settings > Developer options > Disable > Info dialog
+ // CATEGORY: SETTINGS
+ // OS: Q
+ DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
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/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 8d691ff..12e7376 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -273,33 +273,6 @@
private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")");
-
- // If the touchscreen event is within the magnified portion of the screen we have
- // to change its location to be where the user thinks he is poking the
- // UI which may have been magnified and panned.
- if (mMagnificationController.isMagnifying()
- && event.isFromSource(SOURCE_TOUCHSCREEN)
- && mMagnificationController.magnificationRegionContains(
- event.getX(), event.getY())) {
- final float scale = mMagnificationController.getScale();
- final float scaledOffsetX = mMagnificationController.getOffsetX();
- final float scaledOffsetY = mMagnificationController.getOffsetY();
- final int pointerCount = event.getPointerCount();
- PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
- PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
- pointerCount);
- for (int i = 0; i < pointerCount; i++) {
- event.getPointerCoords(i, coords[i]);
- coords[i].x = (coords[i].x - scaledOffsetX) / scale;
- coords[i].y = (coords[i].y - scaledOffsetY) / scale;
- event.getPointerProperties(i, properties[i]);
- }
- event = MotionEvent.obtain(event.getDownTime(),
- event.getEventTime(), event.getAction(), pointerCount, properties,
- coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
- event.getFlags());
- }
if (DEBUG_EVENT_STREAM) {
storeEventInto(mDebugOutputEventHistory, event);
try {
diff --git a/services/art-profile b/services/art-profile
index bdd49de..af9d7a9 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -18849,7 +18849,7 @@
PLcom/android/server/wm/WindowManagerService;->onSystemUiStarted()V
PLcom/android/server/wm/WindowManagerService;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransition(Ljava/lang/String;IILandroid/os/IRemoteCallback;)V
-PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
PLcom/android/server/wm/WindowManagerService;->performBootTimeout()V
PLcom/android/server/wm/WindowManagerService;->performEnableScreen()V
PLcom/android/server/wm/WindowManagerService;->postWindowRemoveCleanupLocked(Lcom/android/server/wm/WindowState;)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 17d8ea7..e8887e7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,11 +18,13 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
+import static android.util.DebugUtils.flagsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -68,13 +70,15 @@
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.infra.AbstractMasterSystemService;
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.Arrays;
import java.util.List;
@@ -95,6 +99,28 @@
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.
+ */
+ // NOTE: must be public because of flagsToString()
+ public static final int FLAG_SMART_SUGGESTION_IME = 0x1;
+
+ /**
+ * System supports Smarts Suggestions (as a popup-window similar to standard Autofill).
+ */
+ // NOTE: must be public because of flagsToString()
+ public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
+ FLAG_SMART_SUGGESTION_IME,
+ FLAG_SMART_SUGGESTION_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SmartSuggestionMode {}
+
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -102,7 +128,6 @@
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
-
/**
* Maximum number of partitions that can be allowed in a session.
*
@@ -130,6 +155,7 @@
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
private final LocalService mLocalService = new LocalService();
+ private final ActivityManagerInternal mAm;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -149,17 +175,21 @@
}
};
- // TODO(b/117779333): move to superclass / create super-class for ShellCommand
+ /**
+ * Supported modes for Augmented Autofill Smart Suggestions.
+ */
@GuardedBy("mLock")
- private boolean mAllowInstantService;
+ private int mSupportedSmartSuggestionModes;
public AutofillManagerService(Context context) {
super(context, UserManager.DISALLOW_AUTOFILL);
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
+ mAm = LocalServices.getService(ActivityManagerInternal.class);
setLogLevelFromSettings();
setMaxPartitionsFromSettings();
setMaxVisibleDatasetsFromSettings();
+ setSmartSuggestionEmulationFromSettings();
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -186,6 +216,9 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS), false, observer,
+ UserHandle.USER_ALL);
}
@Override // from AbstractMasterSystemService
@@ -200,6 +233,9 @@
case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
setMaxVisibleDatasetsFromSettings();
break;
+ case Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS:
+ setSmartSuggestionEmulationFromSettings();
+ break;
default:
Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
// fall through
@@ -231,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());
@@ -243,10 +284,19 @@
mUi.hideAll(null);
}
+ @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+ 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) {
@@ -269,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<>();
@@ -296,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());
@@ -307,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 {
@@ -344,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;
@@ -355,7 +405,7 @@
// Called by Shell command.
int getMaxPartitions() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
return sPartitionMaxCount;
@@ -364,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 {
@@ -389,7 +439,7 @@
// Called by Shell command.
int getMaxVisibleDatasets() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (sLock) {
return sVisibleDatasetsMaxCount;
@@ -398,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 {
@@ -420,10 +470,23 @@
}
}
+ private void setSmartSuggestionEmulationFromSettings() {
+ final int flags = Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0);
+ if (sDebug) {
+ Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): "
+ + smartSuggestionFlagsToString(flags));
+ }
+
+ synchronized (mLock) {
+ mSupportedSmartSuggestionModes = flags;
+ }
+ }
+
// Called by Shell command.
void getScore(@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);
@@ -434,30 +497,46 @@
// 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();
+ }
}
}
@@ -610,6 +689,10 @@
}
}
+ static String smartSuggestionFlagsToString(int flags) {
+ return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags);
+ }
+
private final class LocalService extends AutofillManagerInternal {
@Override
public void onBackKeyPressed() {
@@ -832,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);
@@ -1157,7 +1235,10 @@
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));
+ }
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 67ccc9b..5a0d12c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -70,10 +70,15 @@
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.AbstractRemoteService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.infra.SecureSettingsServiceNameResolver;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -150,10 +155,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;
@@ -161,6 +180,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);
}
@@ -268,8 +293,8 @@
pruneAbandonedSessionsLocked();
final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
- appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed,
- flags);
+ appCallbackToken, hasCallback, componentName, compatMode,
+ bindInstantServiceAllowed, flags);
if (newSession == null) {
return NO_SESSION;
}
@@ -368,7 +393,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,
@@ -823,6 +848,12 @@
return true;
}
+ @GuardedBy("mLock")
+ @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+ // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master)
+ return mMaster.getSupportedSmartSuggestionModesLocked();
+ }
+
@Override
@GuardedBy("mLock")
protected void dumpLocked(String prefix, PrintWriter pw) {
@@ -840,10 +871,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: ");
@@ -962,6 +997,9 @@
if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
session.forceRemoveSelfLocked();
}
+ else {
+ session.destroyAugmentedAutofillWindowsLocked();
+ }
}
}
@@ -982,6 +1020,52 @@
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(
+ getContext(), 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(
+ AbstractRemoteService<? extends AbstractRemoteService<?>> 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..35c5102 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;
@@ -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/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
new file mode 100644
index 0000000..222888c
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.IInterface;
+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> {
+
+ 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;
+
+ private final RemoteAugmentedAutofillServiceCallbacks mCallbacks;
+ private IAugmentedAutofillService mService;
+
+ RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
+ int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
+ boolean bindInstantServiceAllowed, boolean verbose) {
+ super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks,
+ bindInstantServiceAllowed, verbose);
+ mCallbacks = callbacks;
+ }
+
+ @Nullable
+ public static ComponentName getComponentName(@NonNull Context context,
+ @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 IInterface getServiceInterface(IBinder service) {
+ mService = IAugmentedAutofillService.Stub.asInterface(service);
+ return mService;
+ }
+
+ @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) {
+ cancelScheduledUnbind();
+ 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) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+ }
+
+ private abstract static class MyPendingRequest
+ extends PendingRequest<RemoteAugmentedAutofillService> {
+ 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();
+ }
+
+ }
+
+ private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
+
+ protected PendingDestroyAutofillWindowsRequest(
+ @NonNull RemoteAugmentedAutofillService service, @NonNull int sessionId) {
+ super(service, sessionId);
+ }
+
+ @Override
+ public void run() {
+ final RemoteAugmentedAutofillService remoteService = getService();
+ if (remoteService == null) return;
+
+ try {
+ remoteService.mService.onDestroyFillWindowRequest(mSessionId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception handling onDestroyAutofillWindowsRequest() for "
+ + mSessionId + ": " + e);
+ } finally {
+ // Service is not calling back, so we finish right away.
+ finish();
+ }
+ }
+
+ @Override
+ protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
+ // Should not happen because we called finish() on run(), although currently it might
+ // be called if the service is destroyed while showing it.
+ Slog.e(TAG, "timed out: " + this);
+ }
+ }
+
+ public interface RemoteAugmentedAutofillServiceCallbacks extends VultureCallback {
+ // 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 9aa9d7c..4b7d290 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -40,9 +40,9 @@
import android.text.format.DateUtils;
import android.util.Slog;
-import com.android.server.AbstractRemoteService;
+import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
-final class RemoteFillService extends AbstractRemoteService {
+final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
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;
@@ -69,8 +69,8 @@
mCallbacks = callbacks;
}
- @Override
- protected void onConnectedStateChanged(boolean state) {
+ @Override // from AbstractRemoteService
+ protected void handleOnConnectedStateChanged(boolean state) {
if (mAutoFillService == null) {
Slog.w(mTag, "onConnectedStateChanged(): null service");
return;
@@ -82,18 +82,18 @@
}
}
- @Override
+ @Override // from AbstractRemoteService
protected IInterface getServiceInterface(IBinder service) {
mAutoFillService = IAutoFillService.Stub.asInterface(service);
return mAutoFillService;
}
- @Override
+ @Override // from AbstractRemoteService
protected long getTimeoutIdleBindMillis() {
return TIMEOUT_IDLE_BIND_MILLIS;
}
- @Override
+ @Override // from AbstractRemoteService
protected long getRemoteRequestMillis() {
return TIMEOUT_REMOTE_REQUEST_MILLIS;
}
@@ -136,6 +136,19 @@
scheduleRequest(new PendingSaveRequest(request, this));
}
+ private boolean handleResponseCallbackCommon(
+ @NonNull PendingRequest<RemoteFillService> pendingRequest) {
+ if (isDestroyed()) return false;
+
+ if (mPendingRequest == pendingRequest) {
+ mPendingRequest = null;
+ }
+ if (mPendingRequest == null) {
+ scheduleUnbind();
+ }
+ return true;
+ }
+
private void dispatchOnFillRequestSuccess(@NonNull PendingFillRequest pendingRequest,
@Nullable FillResponse response, int requestFlags) {
mHandler.post(() -> {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1ff1acd..d76a5df 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -25,6 +25,9 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM;
+import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString;
import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -92,9 +95,10 @@
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.infra.AbstractRemoteService;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -243,6 +247,19 @@
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 Runnable mAugmentedAutofillDestroyer;
+
+ /**
* Receiver of assist data from the app's {@link Activity}.
*/
private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() {
@@ -892,7 +909,7 @@
// VultureCallback
@Override
- public void onServiceDied(AbstractRemoteService service) {
+ public void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
Slog.w(TAG, "removing session because service died");
forceRemoveSelfLocked();
}
@@ -2497,15 +2514,91 @@
processResponseLocked(newResponse, newClientState, 0);
}
+ @GuardedBy("mLock")
private void processNullResponseLocked(int flags) {
- if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null");
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
mService.resetLastResponse();
- // Nothing to be done, but need to notify client.
- notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
- removeSelf();
+
+ // The default autofill service cannot fullfill the request, let's check if the intelligence
+ // service can.
+ mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked();
+ if (mAugmentedAutofillDestroyer == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "canceling session " + id + " when server returned null and there is no"
+ + " AugmentedAutofill for user");
+ }
+ // Nothing to be done, but need to notify client.
+ notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
+ removeSelf();
+ } else {
+ // TODO(b/111330312, b/119638958): must set internal state so when user focus other
+ // fields it does not generate a new call to the standard autofill service (right now
+ // it does). Must also add CTS tests to exercise this scenario.
+ if (sVerbose) {
+ Slog.v(TAG, "keeping session " + id + " when server returned null but "
+ + "there is an AugmentedAutofill for user");
+ }
+ }
+ }
+
+ /**
+ * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
+ *
+ * @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 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 RemoteAugmentedAutofillService remoteService = mService
+ .getRemoteAugmentedAutofillServiceLocked();
+ if (remoteService == null) return null;
+
+ // Define which mode will be used
+ final int mode;
+ if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) {
+ // TODO(b/111330312): support it :-)
+ Slog.w(TAG, "Smart Suggestions on IME not supported yet");
+ return null;
+ } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
+ mode = FLAG_SMART_SUGGESTION_SYSTEM;
+ } else {
+ Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes);
+ return null;
+ }
+
+ if (mCurrentViewId == null) {
+ Slog.w(TAG, "triggerAugmentedAutofillLocked(): no view currently focused");
+ return null;
+ }
+
+ if (sVerbose) {
+ Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId
+ + " using suggestion mode " + smartSuggestionFlagsToString(mode)
+ + " 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
+ remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId,
+ currentValue);
+
+ if (mAugmentedAutofillDestroyer == null) {
+ mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id);
+ }
+ return mAugmentedAutofillDestroyer;
}
@GuardedBy("mLock")
@@ -2786,6 +2879,9 @@
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+ if (mAugmentedAutofillDestroyer != null) {
+ pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
+ }
mRemoteFillService.dump(prefix, pw);
}
@@ -2957,6 +3053,15 @@
Slog.e(TAG, "Error notifying client to finish session", e);
}
}
+ destroyAugmentedAutofillWindowsLocked();
+ }
+
+ @GuardedBy("mLock")
+ void destroyAugmentedAutofillWindowsLocked() {
+ if (mAugmentedAutofillDestroyer != null) {
+ mAugmentedAutofillDestroyer.run();
+ mAugmentedAutofillDestroyer = null;
+ }
}
/**
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 645723e..d1dbbff 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,5 @@
anniemeng@google.com
-artikz@google.com
brufino@google.com
bryanmawhinney@google.com
ctate@google.com
jorlow@google.com
-mkarpinski@google.com
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index df46d260b..2bca34d 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -16,12 +16,15 @@
package com.android.server.backup;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
import android.content.ContentResolver;
import android.os.Handler;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.KeyValueSettingObserver;
import android.util.Slog;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -137,7 +140,7 @@
public long getKvBackupAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
}
return mKvBackupAgentTimeoutMillis;
@@ -146,7 +149,7 @@
public long getFullBackupAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
}
return mFullBackupAgentTimeoutMillis;
@@ -155,7 +158,7 @@
public long getSharedBackupAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
@@ -166,7 +169,7 @@
public long getRestoreAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
}
return mRestoreAgentTimeoutMillis;
@@ -175,7 +178,7 @@
public long getRestoreAgentFinishedTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getRestoreAgentFinishedTimeoutMillis(): "
@@ -187,7 +190,7 @@
public long getQuotaExceededTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getQuotaExceededTimeoutMillis(): "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index ec21961..785d3ca 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -16,6 +16,8 @@
package com.android.server.backup;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.os.Handler;
@@ -24,6 +26,7 @@
import android.util.KeyValueListParser;
import android.util.KeyValueSettingObserver;
import android.util.Slog;
+
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -151,7 +154,7 @@
// group the calls of these methods in a block syncrhonized on
// a reference of this object.
public synchronized long getKeyValueBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupIntervalMilliseconds(...) returns "
@@ -161,7 +164,7 @@
}
public synchronized long getKeyValueBackupFuzzMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupFuzzMilliseconds(...) returns "
@@ -171,7 +174,7 @@
}
public synchronized boolean getKeyValueBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupRequireCharging(...) returns "
@@ -181,7 +184,7 @@
}
public synchronized int getKeyValueBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupRequiredNetworkType(...) returns "
@@ -191,7 +194,7 @@
}
public synchronized long getFullBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getFullBackupIntervalMilliseconds(...) returns "
@@ -201,14 +204,14 @@
}
public synchronized boolean getFullBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
}
return mFullBackupRequireCharging;
}
public synchronized int getFullBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getFullBackupRequiredNetworkType(...) returns "
@@ -219,7 +222,7 @@
/** Returns an array of package names that should be notified whenever a backup finishes. */
public synchronized String[] getBackupFinishedNotificationReceivers() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getBackupFinishedNotificationReceivers(...) returns "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e4b4bc5..0b06f28 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.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.
@@ -16,239 +16,62 @@
package com.android.server.backup;
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
-
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
-import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IBackupAgent;
-import android.app.PendingIntent;
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupManager;
-import android.app.backup.BackupManagerMonitor;
-import android.app.backup.FullBackup;
-import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.app.job.JobParameters;
+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.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-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.Bundle;
import android.os.Environment;
-import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerSaveState;
-import android.os.Process;
import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.WorkSource;
-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;
-import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.server.AppWidgetBackupBridge;
-import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.backup.fullbackup.FullBackupEntry;
-import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
-import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.internal.ClearDataObserver;
-import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
-import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.ProvisionedObserver;
-import com.android.server.backup.internal.RunBackupReceiver;
-import com.android.server.backup.internal.RunInitializeReceiver;
-import com.android.server.backup.keyvalue.BackupRequest;
-import com.android.server.backup.params.AdbBackupParams;
-import com.android.server.backup.params.AdbParams;
-import com.android.server.backup.params.AdbRestoreParams;
-import com.android.server.backup.params.BackupParams;
-import com.android.server.backup.params.ClearParams;
-import com.android.server.backup.params.ClearRetryParams;
-import com.android.server.backup.params.RestoreParams;
-import com.android.server.backup.restore.ActiveRestoreSession;
-import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.backup.utils.AppBackupUtils;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
-import com.android.server.backup.utils.BackupObserverUtils;
-import com.android.server.backup.utils.SparseArrayUtils;
-import com.google.android.collect.Sets;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Random;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
-/** System service that performs backup/restore operations. */
+/**
+ * Definition of the system service that performs backup/restore operations.
+ *
+ * <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
+ * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
+ * corresponding backup/restore operation.
+ */
public class BackupManagerService {
public static final String TAG = "BackupManagerService";
public static final boolean DEBUG = true;
public static final boolean MORE_DEBUG = false;
public static final boolean DEBUG_SCHEDULING = true;
- // File containing backup-enabled state. Contains a single byte;
- // nonzero == enabled. File missing or contains a zero byte == disabled.
+ // File containing backup-enabled state. Contains a single byte to denote enabled status.
+ // Nonzero is enabled; file missing or a zero byte is disabled.
private static final String BACKUP_ENABLE_FILE = "backup_enabled";
- // Persistently track the need to do a full init.
- private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
-
- // 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).
- public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
-
- // Name and current contents version of the full-backup manifest file
- //
- // Manifest version history:
- //
- // 1 : initial release
- public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
- public static final int BACKUP_MANIFEST_VERSION = 1;
-
- // External archive format version history:
- //
- // 1 : initial release
- // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
- // 3 : introduced "_meta" metadata file; no other format change per se
- // 4 : added support for new device-encrypted storage locations
- // 5 : added support for key-value packages
- public static final int BACKUP_FILE_VERSION = 5;
- public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
- public static final String BACKUP_METADATA_FILENAME = "_meta";
- public static final int BACKUP_METADATA_VERSION = 1;
- public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
-
- private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
-
- // Round-robin queue for scheduling full backup passes.
- private static final int SCHEDULE_FILE_VERSION = 1;
-
- public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
- public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
-
- // Pseudoname that we use for the Package Manager metadata "package".
- public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-
- // Retry interval for clear/init when the transport is unavailable
- private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
-
- public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
- public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
- public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
- public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
-
- // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
- // pending operations list.
- public static final int OP_PENDING = 0;
- private static final int OP_ACKNOWLEDGED = 1;
- private static final int OP_TIMEOUT = -1;
-
- // Waiting for backup agent to respond during backup operation.
- public static final int OP_TYPE_BACKUP_WAIT = 0;
-
- // Waiting for backup agent to respond during restore operation.
- public static final int OP_TYPE_RESTORE_WAIT = 1;
-
- // An entire backup operation spanning multiple packages.
- public static final int OP_TYPE_BACKUP = 2;
-
- // Time delay for initialization operations that can be delayed so as not to consume too much
- // CPU on bring-up and increase time-to-UI.
- private static final long INITIALIZATION_DELAY_MILLIS = 3000;
-
- // Timeout interval for deciding that a bind or clear-data has taken too long
- private static final long TIMEOUT_INTERVAL = 10 * 1000;
-
- // User confirmation timeout for a full backup/restore operation. It's this long in
- // order to give them time to enter the backup password.
- private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
-
- // If an app is busy when we want to do a full-data backup, how long to defer the retry.
- // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
- private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
- private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
-
// The published binder is a singleton Trampoline object that calls through to the proper code.
// This indirection lets us turn down the heavy implementation object on the fly without
// disturbing binders that have been cached elsewhere in the system.
@@ -302,483 +125,25 @@
transportManager);
}
- private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- private final TransportManager mTransportManager;
+ private UserBackupManagerService mUserBackupManagerService;
- private Context mContext;
- private PackageManager mPackageManager;
- private IPackageManager mPackageManagerBinder;
- private IActivityManager mActivityManager;
- private PowerManager mPowerManager;
- private AlarmManager mAlarmManager;
- private IStorageManager mStorageManager;
- private BackupManagerConstants mConstants;
- private PowerManager.WakeLock mWakelock;
- private BackupHandler mBackupHandler;
-
- private IBackupManager mBackupManagerBinder;
-
- private boolean mEnabled; // access to this is synchronized on 'this'
- private boolean mProvisioned;
- private boolean mAutoRestore;
-
- private PendingIntent mRunBackupIntent;
- private PendingIntent mRunInitIntent;
-
- private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
-
- // map UIDs to the set of participating packages under that UID
- private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
-
- // Backups that we haven't started yet. Keys are package names.
- private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
-
- // locking around the pending-backup management
- private final Object mQueueLock = new Object();
-
- // The thread performing the sequence of queued backups binds to each app's agent
- // in succession. Bind notifications are asynchronously delivered through the
- // Activity Manager; use this lock object to signal when a requested binding has
- // completed.
- private final Object mAgentConnectLock = new Object();
- private IBackupAgent mConnectedAgent;
- private volatile boolean mConnecting;
-
- private volatile boolean mBackupRunning;
- private volatile long mLastBackupPass;
-
- // A similar synchronization mechanism around clearing apps' data for restore
- private final Object mClearDataLock = new Object();
- private volatile boolean mClearingData;
-
- // Used by ADB.
- private final BackupPasswordManager mBackupPasswordManager;
- private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
- private final SecureRandom mRng = new SecureRandom();
-
- // Time when we post the transport registration operation
- private final long mRegisterTransportsRequestedTime;
-
- @GuardedBy("mQueueLock")
- private PerformFullTransportBackupTask mRunningFullBackupTask;
-
- @GuardedBy("mQueueLock")
- private ArrayList<FullBackupEntry> mFullBackupQueue;
-
- @GuardedBy("mPendingRestores")
- private boolean mIsRestoreInProgress;
-
- @GuardedBy("mPendingRestores")
- private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
-
- private ActiveRestoreSession mActiveRestoreSession;
-
- // Watch the device provisioning operation during setup
- private ContentObserver mProvisionedObserver;
-
- /**
- * mCurrentOperations contains the list of currently active operations.
- *
- * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
- * An operation wraps a BackupRestoreTask within it.
- * It's the responsibility of this task to remove the operation from this array.
- *
- * A BackupRestore task gets notified of ack/timeout for the operation via
- * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
- * on the mCurrentOpLock.
- * {@link BackupManagerService#waitUntilOperationComplete(int)} is
- * used in various places to 'wait' for notifyAll and detect change of pending state of an
- * operation. So typically, an operation will be removed from this array by:
- * - BackupRestoreTask#handleCancel and
- * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
- * these places because waitUntilOperationComplete relies on the operation being present to
- * determine its completion status.
- *
- * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
- * cancel backup tasks.
- */
- @GuardedBy("mCurrentOpLock")
- private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
- private final Object mCurrentOpLock = new Object();
- private final Random mTokenGenerator = new Random();
- final AtomicInteger mNextToken = new AtomicInteger();
-
- // Where we keep our journal files and other bookkeeping.
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
- @Nullable
- private DataChangedJournal mJournal;
- private File mFullBackupScheduleFile;
-
- // Keep a log of all the apps we've ever backed up.
- private ProcessedPackagesJournal mProcessedPackagesJournal;
-
- private File mTokenFile;
- private Set<String> mAncestralPackages = null;
- private long mAncestralToken = 0;
- private long mCurrentToken = 0;
-
- @VisibleForTesting
+ /** Instantiate a new instance of {@link BackupManagerService}. */
public BackupManagerService(
Context context,
- Trampoline parent,
+ Trampoline trampoline,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
- mContext = context;
- mPackageManager = context.getPackageManager();
- mPackageManagerBinder = AppGlobals.getPackageManager();
- mActivityManager = ActivityManager.getService();
-
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
-
- mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
-
- mAgentTimeoutParameters = new
- BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
- mAgentTimeoutParameters.start();
-
- // spin up the backup/restore handler thread
- mBackupHandler = new BackupHandler(this, backupThread.getLooper());
-
- // Set up our bookkeeping
- final ContentResolver resolver = context.getContentResolver();
- mProvisioned = Settings.Global.getInt(resolver,
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- mAutoRestore = Settings.Secure.getInt(resolver,
- Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
-
- mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
- resolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
- false, mProvisionedObserver);
-
- mBaseStateDir = baseStateDir;
- mBaseStateDir.mkdirs();
- if (!SELinux.restorecon(mBaseStateDir)) {
- Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
- }
-
- mDataDir = dataDir;
-
- mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
-
- // Alarm receivers for scheduled backups & initialization operations
- BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
- IntentFilter filter = new IntentFilter();
- filter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiver(mRunBackupReceiver, filter,
- android.Manifest.permission.BACKUP, null);
-
- BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
- filter = new IntentFilter();
- filter.addAction(RUN_INITIALIZE_ACTION);
- context.registerReceiver(mRunInitReceiver, filter,
- android.Manifest.permission.BACKUP, null);
-
- Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
- backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
-
- Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
- initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
-
- // Set up the backup-request journaling
- mJournalDir = new File(mBaseStateDir, "pending");
- mJournalDir.mkdirs(); // creates mBaseStateDir along the way
- mJournal = null; // will be created on first use
-
- mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
- // We are observing changes to the constants throughout the lifecycle of BMS. This is
- // because we reference the constants in multiple areas of BMS, which otherwise would
- // require frequent starting and stopping.
- mConstants.start();
-
- // Set up the various sorts of package tracking we do
- mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
- initPackageTracking();
-
- // Build our mapping of uid to backup client services. This implicitly
- // schedules a backup pass on the Package Manager metadata the first
- // time anything needs to be backed up.
- synchronized (mBackupParticipants) {
- addPackageParticipantsLocked(null);
- }
-
- mTransportManager = transportManager;
- mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
- mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
- mBackupHandler.postDelayed(
- mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
-
- // Now that we know about valid backup participants, parse any leftover journal files into
- // the pending backup set
- mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
-
- // Power management
- mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+ mUserBackupManagerService =
+ new UserBackupManagerService(
+ context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
}
-
- public BackupManagerConstants getConstants() {
- return mConstants;
- }
-
- public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
- return mAgentTimeoutParameters;
- }
-
- public Context getContext() {
- return mContext;
- }
-
- public void setContext(Context context) {
- mContext = context;
- }
-
- public PackageManager getPackageManager() {
- return mPackageManager;
- }
-
- public void setPackageManager(PackageManager packageManager) {
- mPackageManager = packageManager;
- }
-
- public IPackageManager getPackageManagerBinder() {
- return mPackageManagerBinder;
- }
-
- public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
- mPackageManagerBinder = packageManagerBinder;
- }
-
- public IActivityManager getActivityManager() {
- return mActivityManager;
- }
-
- public void setActivityManager(IActivityManager activityManager) {
- mActivityManager = activityManager;
- }
-
- public AlarmManager getAlarmManager() {
- return mAlarmManager;
- }
-
- public void setAlarmManager(AlarmManager alarmManager) {
- mAlarmManager = alarmManager;
- }
-
+ // TODO(b/118520567): Remove when tests are modified to use per-user instance.
@VisibleForTesting
- void setPowerManager(PowerManager powerManager) {
- mPowerManager = powerManager;
- }
-
- public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
- mBackupManagerBinder = backupManagerBinder;
- }
-
- public TransportManager getTransportManager() {
- return mTransportManager;
- }
-
- public boolean isEnabled() {
- return mEnabled;
- }
-
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- public boolean isProvisioned() {
- return mProvisioned;
- }
-
- public void setProvisioned(boolean provisioned) {
- mProvisioned = provisioned;
- }
-
- public PowerManager.WakeLock getWakelock() {
- return mWakelock;
- }
-
- /**
- * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
- * #getWakelock()}.
- */
- @VisibleForTesting
- public void setWorkSource(@Nullable WorkSource workSource) {
- // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
- mWakelock.setWorkSource(workSource);
- }
-
- public void setWakelock(PowerManager.WakeLock wakelock) {
- mWakelock = wakelock;
- }
-
- public Handler getBackupHandler() {
- return mBackupHandler;
- }
-
- public void setBackupHandler(BackupHandler backupHandler) {
- mBackupHandler = backupHandler;
- }
-
- public PendingIntent getRunInitIntent() {
- return mRunInitIntent;
- }
-
- public void setRunInitIntent(PendingIntent runInitIntent) {
- mRunInitIntent = runInitIntent;
- }
-
- public HashMap<String, BackupRequest> getPendingBackups() {
- return mPendingBackups;
- }
-
- public void setPendingBackups(
- HashMap<String, BackupRequest> pendingBackups) {
- mPendingBackups = pendingBackups;
- }
-
- public Object getQueueLock() {
- return mQueueLock;
- }
-
- public boolean isBackupRunning() {
- return mBackupRunning;
- }
-
- public void setBackupRunning(boolean backupRunning) {
- mBackupRunning = backupRunning;
- }
-
- public long getLastBackupPass() {
- return mLastBackupPass;
- }
-
- public void setLastBackupPass(long lastBackupPass) {
- mLastBackupPass = lastBackupPass;
- }
-
- public Object getClearDataLock() {
- return mClearDataLock;
- }
-
- public boolean isClearingData() {
- return mClearingData;
- }
-
- public void setClearingData(boolean clearingData) {
- mClearingData = clearingData;
- }
-
- public boolean isRestoreInProgress() {
- return mIsRestoreInProgress;
- }
-
- public void setRestoreInProgress(boolean restoreInProgress) {
- mIsRestoreInProgress = restoreInProgress;
- }
-
- public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
- return mPendingRestores;
- }
-
- public ActiveRestoreSession getActiveRestoreSession() {
- return mActiveRestoreSession;
- }
-
- public void setActiveRestoreSession(
- ActiveRestoreSession activeRestoreSession) {
- mActiveRestoreSession = activeRestoreSession;
- }
-
- public SparseArray<Operation> getCurrentOperations() {
- return mCurrentOperations;
- }
-
- public Object getCurrentOpLock() {
- return mCurrentOpLock;
- }
-
- public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
- return mAdbBackupRestoreConfirmations;
- }
-
- public File getBaseStateDir() {
- return mBaseStateDir;
- }
-
- public void setBaseStateDir(File baseStateDir) {
- mBaseStateDir = baseStateDir;
- }
-
- public File getDataDir() {
- return mDataDir;
- }
-
- public void setDataDir(File dataDir) {
- mDataDir = dataDir;
- }
-
- @Nullable
- public DataChangedJournal getJournal() {
- return mJournal;
- }
-
- public void setJournal(@Nullable DataChangedJournal journal) {
- mJournal = journal;
- }
-
- public SecureRandom getRng() {
- return mRng;
- }
-
- public Set<String> getAncestralPackages() {
- return mAncestralPackages;
- }
-
- public void setAncestralPackages(Set<String> ancestralPackages) {
- mAncestralPackages = ancestralPackages;
- }
-
- public long getAncestralToken() {
- return mAncestralToken;
- }
-
- public void setAncestralToken(long ancestralToken) {
- mAncestralToken = ancestralToken;
- }
-
- public long getCurrentToken() {
- return mCurrentToken;
- }
-
- public void setCurrentToken(long currentToken) {
- mCurrentToken = currentToken;
- }
-
- public ArraySet<String> getPendingInits() {
- return mPendingInits;
- }
-
- /** Clear all pending transport initializations. */
- public void clearPendingInits() {
- mPendingInits.clear();
- }
-
- public PerformFullTransportBackupTask getRunningFullBackupTask() {
- return mRunningFullBackupTask;
- }
-
- public void setRunningFullBackupTask(
- PerformFullTransportBackupTask runningFullBackupTask) {
- mRunningFullBackupTask = runningFullBackupTask;
+ void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
+ mUserBackupManagerService = userBackupManagerService;
}
/**
@@ -819,2092 +184,93 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- /**
- * Utility: build a new random integer token. The low bits are the ordinal of the operation for
- * near-time uniqueness, and the upper bits are random for app-side unpredictability.
+ /*
+ * The following methods are implementations of IBackupManager methods called from Trampoline.
+ * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
+ * action on the passed in user. Currently this is a straight redirection (see TODO).
*/
- public int generateRandomIntegerToken() {
- int token = mTokenGenerator.nextInt();
- if (token < 0) token = -token;
- token &= ~0xFF;
- token |= (mNextToken.incrementAndGet() & 0xFF);
- return token;
+ // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+
+ // ---------------------------------------------
+ // BACKUP AGENT OPERATIONS
+ // ---------------------------------------------
+
+ /**
+ * An app's backup agent calls this method to let the service know that there's new data to
+ * backup for their app {@code packageName}. Only used for apps participating in key-value
+ * backup.
+ */
+ public void dataChanged(String packageName) {
+ mUserBackupManagerService.dataChanged(packageName);
}
/**
- * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
- * non-lifecycle agent instance, so we manually set up the context topology for it.
+ * Callback: a requested backup agent has been instantiated. This should only be called from the
+ * {@link ActivityManager}.
*/
- public BackupAgent makeMetadataAgent() {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
- pmAgent.attach(mContext);
- pmAgent.onCreate();
- return pmAgent;
+ public void agentConnected(String packageName, IBinder agentBinder) {
+ mUserBackupManagerService.agentConnected(packageName, agentBinder);
}
/**
- * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+ * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+ * called from the {@link ActivityManager}.
*/
- public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
- PackageManagerBackupAgent pmAgent =
- new PackageManagerBackupAgent(mPackageManager, packages);
- pmAgent.attach(mContext);
- pmAgent.onCreate();
- return pmAgent;
- }
-
- private void initPackageTracking() {
- if (MORE_DEBUG) Slog.v(TAG, "` tracking");
-
- // Remember our ancestral dataset
- mTokenFile = new File(mBaseStateDir, "ancestral");
- try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
- new FileInputStream(mTokenFile)))) {
- int version = tokenStream.readInt();
- if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
- mAncestralToken = tokenStream.readLong();
- mCurrentToken = tokenStream.readLong();
-
- int numPackages = tokenStream.readInt();
- if (numPackages >= 0) {
- mAncestralPackages = new HashSet<>();
- for (int i = 0; i < numPackages; i++) {
- String pkgName = tokenStream.readUTF();
- mAncestralPackages.add(pkgName);
- }
- }
- }
- } catch (FileNotFoundException fnf) {
- // Probably innocuous
- Slog.v(TAG, "No ancestral data");
- } catch (IOException e) {
- Slog.w(TAG, "Unable to read token file", e);
- }
-
- mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
- mProcessedPackagesJournal.init();
-
- synchronized (mQueueLock) {
- // Resume the full-data backup queue
- mFullBackupQueue = readFullBackupSchedule();
- }
-
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
- // Register for events related to sdcard installation.
- IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
- }
-
- private ArrayList<FullBackupEntry> readFullBackupSchedule() {
- boolean changed = false;
- ArrayList<FullBackupEntry> schedule = null;
- List<PackageInfo> apps =
- PackageManagerBackupAgent.getStorableApplications(mPackageManager);
-
- if (mFullBackupScheduleFile.exists()) {
- try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
- BufferedInputStream bufStream = new BufferedInputStream(fstream);
- DataInputStream in = new DataInputStream(bufStream)) {
- int version = in.readInt();
- if (version != SCHEDULE_FILE_VERSION) {
- Slog.e(TAG, "Unknown backup schedule version " + version);
- return null;
- }
-
- final int numPackages = in.readInt();
- schedule = new ArrayList<>(numPackages);
-
- // HashSet instead of ArraySet specifically because we want the eventual
- // lookups against O(hundreds) of entries to be as fast as possible, and
- // we discard the set immediately after the scan so the extra memory
- // overhead is transient.
- HashSet<String> foundApps = new HashSet<>(numPackages);
-
- for (int i = 0; i < numPackages; i++) {
- String pkgName = in.readUTF();
- long lastBackup = in.readLong();
- foundApps.add(pkgName); // all apps that we've addressed already
- try {
- PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
- if (AppBackupUtils.appGetsFullBackup(pkg)
- && AppBackupUtils.appIsEligibleForBackup(
- pkg.applicationInfo, mPackageManager)) {
- schedule.add(new FullBackupEntry(pkgName, lastBackup));
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Package " + pkgName
- + " no longer eligible for full backup");
- }
- }
- } catch (NameNotFoundException e) {
- if (DEBUG) {
- Slog.i(TAG, "Package " + pkgName
- + " not installed; dropping from full backup");
- }
- }
- }
-
- // New apps can arrive "out of band" via OTA and similar, so we also need to
- // scan to make sure that we're tracking all full-backup candidates properly
- for (PackageInfo app : apps) {
- if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
- if (!foundApps.contains(app.packageName)) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "New full backup app " + app.packageName + " found");
- }
- schedule.add(new FullBackupEntry(app.packageName, 0));
- changed = true;
- }
- }
- }
-
- Collections.sort(schedule);
- } catch (Exception e) {
- Slog.e(TAG, "Unable to read backup schedule", e);
- mFullBackupScheduleFile.delete();
- schedule = null;
- }
- }
-
- if (schedule == null) {
- // no prior queue record, or unable to read it. Set up the queue
- // from scratch.
- changed = true;
- schedule = new ArrayList<>(apps.size());
- for (PackageInfo info : apps) {
- if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo, mPackageManager)) {
- schedule.add(new FullBackupEntry(info.packageName, 0));
- }
- }
- }
-
- if (changed) {
- writeFullBackupScheduleAsync();
- }
- return schedule;
- }
-
- private Runnable mFullBackupScheduleWriter = new Runnable() {
- @Override
- public void run() {
- synchronized (mQueueLock) {
- try {
- ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
- DataOutputStream bufOut = new DataOutputStream(bufStream);
- bufOut.writeInt(SCHEDULE_FILE_VERSION);
-
- // version 1:
- //
- // [int] # of packages in the queue = N
- // N * {
- // [utf8] package name
- // [long] last backup time for this package
- // }
- int numPackages = mFullBackupQueue.size();
- bufOut.writeInt(numPackages);
-
- for (int i = 0; i < numPackages; i++) {
- FullBackupEntry entry = mFullBackupQueue.get(i);
- bufOut.writeUTF(entry.packageName);
- bufOut.writeLong(entry.lastBackup);
- }
- bufOut.flush();
-
- AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
- FileOutputStream out = af.startWrite();
- out.write(bufStream.toByteArray());
- af.finishWrite(out);
- } catch (Exception e) {
- Slog.e(TAG, "Unable to write backup schedule!", e);
- }
- }
- }
- };
-
- private void writeFullBackupScheduleAsync() {
- mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
- mBackupHandler.post(mFullBackupScheduleWriter);
- }
-
- private void parseLeftoverJournals() {
- ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
- for (DataChangedJournal journal : journals) {
- if (!journal.equals(mJournal)) {
- try {
- journal.forEach(packageName -> {
- Slog.i(TAG, "Found stale backup journal, scheduling");
- if (MORE_DEBUG) Slog.i(TAG, " " + packageName);
- dataChangedImpl(packageName);
- });
- } catch (IOException e) {
- Slog.e(TAG, "Can't read " + journal, e);
- }
- }
- }
- }
-
- /** Used for generating random salts or passwords. */
- public byte[] randomBytes(int bits) {
- byte[] array = new byte[bits / 8];
- mRng.nextBytes(array);
- return array;
- }
-
- /** For adb backup/restore. */
- public boolean setBackupPassword(String currentPw, String newPw) {
- return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
- }
-
- /** For adb backup/restore. */
- public boolean hasBackupPassword() {
- return mBackupPasswordManager.hasBackupPassword();
- }
-
- /** For adb backup/restore. */
- public boolean backupPasswordMatches(String currentPw) {
- return mBackupPasswordManager.backupPasswordMatches(currentPw);
+ public void agentDisconnected(String packageName) {
+ mUserBackupManagerService.agentDisconnected(packageName);
}
/**
- * Maintain persistent state around whether need to do an initialize operation. This will lock
- * on {@link #getQueueLock()}.
+ * Used by a currently-active backup agent to notify the service that it has completed its given
+ * outstanding asynchronous backup/restore operation.
*/
- public void recordInitPending(
- boolean isPending, String transportName, String transportDirName) {
- synchronized (mQueueLock) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
- }
-
- File stateDir = new File(mBaseStateDir, transportDirName);
- File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-
- if (isPending) {
- // We need an init before we can proceed with sending backup data.
- // Record that with an entry in our set of pending inits, as well as
- // journaling it via creation of a sentinel file.
- mPendingInits.add(transportName);
- try {
- (new FileOutputStream(initPendingFile)).close();
- } catch (IOException ioe) {
- // Something is badly wrong with our permissions; just try to move on
- }
- } else {
- // No more initialization needed; wipe the journal and reset our state.
- initPendingFile.delete();
- mPendingInits.remove(transportName);
- }
- }
+ public void opComplete(int token, long result) {
+ mUserBackupManagerService.opComplete(token, result);
}
- /**
- * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
- * expiry), so we must re-upload all saved settings.
- */
- public void resetBackupState(File stateFileDir) {
- synchronized (mQueueLock) {
- mProcessedPackagesJournal.reset();
+ // ---------------------------------------------
+ // TRANSPORT OPERATIONS
+ // ---------------------------------------------
- mCurrentToken = 0;
- writeRestoreTokens();
-
- // Remove all the state files
- for (File sf : stateFileDir.listFiles()) {
- // ... but don't touch the needs-init sentinel
- if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
- sf.delete();
- }
- }
- }
-
- // Enqueue a new backup of every participant
- synchronized (mBackupParticipants) {
- final int numParticipants = mBackupParticipants.size();
- for (int i = 0; i < numParticipants; i++) {
- HashSet<String> participants = mBackupParticipants.valueAt(i);
- if (participants != null) {
- for (String packageName : participants) {
- dataChangedImpl(packageName);
- }
- }
- }
- }
- }
-
- private void onTransportRegistered(String transportName, String transportDirName) {
- if (DEBUG) {
- long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
- Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
- + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
- }
-
- File stateDir = new File(mBaseStateDir, transportDirName);
- stateDir.mkdirs();
-
- File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
- if (initSentinel.exists()) {
- synchronized (mQueueLock) {
- mPendingInits.add(transportName);
-
- // TODO: pick a better starting time than now + 1 minute
- long delay = 1000 * 60; // one minute, in milliseconds
- mAlarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + delay, mRunInitIntent);
- }
- }
- }
-
- // ----- Track installation/removal of packages -----
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
-
- String action = intent.getAction();
- boolean replacing = false;
- boolean added = false;
- boolean changed = false;
- Bundle extras = intent.getExtras();
- String[] pkgList = null;
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_REMOVED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- final String pkgName = uri.getSchemeSpecificPart();
- if (pkgName != null) {
- pkgList = new String[]{pkgName};
- }
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
-
- // At package-changed we only care about looking at new transport states
- if (changed) {
- final String[] components =
- intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
-
- if (MORE_DEBUG) {
- Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
- for (int i = 0; i < components.length; i++) {
- Slog.i(TAG, " * " + components[i]);
- }
- }
-
- mBackupHandler.post(
- () -> mTransportManager.onPackageChanged(pkgName, components));
- return; // nothing more to do in the PACKAGE_CHANGED case
- }
-
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- added = true;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- added = false;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- }
-
- if (pkgList == null || pkgList.length == 0) {
- return;
- }
-
- final int uid = extras.getInt(Intent.EXTRA_UID);
- if (added) {
- synchronized (mBackupParticipants) {
- if (replacing) {
- // This is the package-replaced case; we just remove the entry
- // under the old uid and fall through to re-add. If an app
- // just added key/value backup participation, this picks it up
- // as a known participant.
- removePackageParticipantsLocked(pkgList, uid);
- }
- addPackageParticipantsLocked(pkgList);
- }
- // If they're full-backup candidates, add them there instead
- final long now = System.currentTimeMillis();
- for (final String packageName : pkgList) {
- try {
- PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
- if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
- enqueueFullBackup(packageName, now);
- scheduleNextFullBackupJob(0);
- } else {
- // The app might have just transitioned out of full-data into
- // doing key/value backups, or might have just disabled backups
- // entirely. Make sure it is no longer in the full-data queue.
- synchronized (mQueueLock) {
- dequeueFullBackupLocked(packageName);
- }
- writeFullBackupScheduleAsync();
- }
-
- mBackupHandler.post(
- () -> mTransportManager.onPackageAdded(packageName));
-
- } catch (NameNotFoundException e) {
- // doesn't really exist; ignore it
- if (DEBUG) {
- Slog.w(TAG, "Can't resolve new app " + packageName);
- }
- }
- }
-
- // Whenever a package is added or updated we need to update
- // the package metadata bookkeeping.
- dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
- } else {
- if (replacing) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- // Outright removal. In the full-data case, the app will be dropped
- // from the queue when its (now obsolete) name comes up again for
- // backup.
- synchronized (mBackupParticipants) {
- removePackageParticipantsLocked(pkgList, uid);
- }
- }
- for (final String pkgName : pkgList) {
- mBackupHandler.post(
- () -> mTransportManager.onPackageRemoved(pkgName));
- }
- }
- }
- };
-
- // Add the backup agents in the given packages to our set of known backup participants.
- // If 'packageNames' is null, adds all backup agents in the whole system.
- private void addPackageParticipantsLocked(String[] packageNames) {
- // Look for apps that define the android:backupAgent attribute
- List<PackageInfo> targetApps = allAgentPackages();
- if (packageNames != null) {
- if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
- for (String packageName : packageNames) {
- addPackageParticipantsLockedInner(packageName, targetApps);
- }
- } else {
- if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
- addPackageParticipantsLockedInner(null, targetApps);
- }
- }
-
- private void addPackageParticipantsLockedInner(String packageName,
- List<PackageInfo> targetPkgs) {
- if (MORE_DEBUG) {
- Slog.v(TAG, "Examining " + packageName + " for backup agent");
- }
-
- for (PackageInfo pkg : targetPkgs) {
- if (packageName == null || pkg.packageName.equals(packageName)) {
- int uid = pkg.applicationInfo.uid;
- HashSet<String> set = mBackupParticipants.get(uid);
- if (set == null) {
- set = new HashSet<>();
- mBackupParticipants.put(uid, set);
- }
- set.add(pkg.packageName);
- if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
-
- // Schedule a backup for it on general principles
- if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
- Message msg = mBackupHandler
- .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
- mBackupHandler.sendMessage(msg);
- }
- }
- }
-
- // Remove the given packages' entries from our known active set.
- private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
- if (packageNames == null) {
- Slog.w(TAG, "removePackageParticipants with null list");
- return;
- }
-
- if (MORE_DEBUG) {
- Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
- + " #" + packageNames.length);
- }
- for (String pkg : packageNames) {
- // Known previous UID, so we know which package set to check
- HashSet<String> set = mBackupParticipants.get(oldUid);
- if (set != null && set.contains(pkg)) {
- removePackageFromSetLocked(set, pkg);
- if (set.isEmpty()) {
- if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
- mBackupParticipants.remove(oldUid);
- }
- }
- }
- }
-
- private void removePackageFromSetLocked(final HashSet<String> set,
- final String packageName) {
- if (set.contains(packageName)) {
- // Found it. Remove this one package from the bookkeeping, and
- // if it's the last participating app under this uid we drop the
- // (now-empty) set as well.
- // Note that we deliberately leave it 'known' in the "ever backed up"
- // bookkeeping so that its current-dataset data will be retrieved
- // if the app is subsequently reinstalled
- if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
- set.remove(packageName);
- mPendingBackups.remove(packageName);
- }
- }
-
- // Returns the set of all applications that define an android:backupAgent attribute
- private List<PackageInfo> allAgentPackages() {
- // !!! TODO: cache this and regenerate only when necessary
- int flags = PackageManager.GET_SIGNING_CERTIFICATES;
- List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
- int numPackages = packages.size();
- for (int a = numPackages - 1; a >= 0; a--) {
- PackageInfo pkg = packages.get(a);
- try {
- ApplicationInfo app = pkg.applicationInfo;
- if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null
- || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
- packages.remove(a);
- } else {
- // we will need the shared library path, so look that up and store it here.
- // This is used implicitly when we pass the PackageInfo object off to
- // the Activity Manager to launch the app for backup/restore purposes.
- app = mPackageManager.getApplicationInfo(pkg.packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES);
- pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
- pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
- }
- } catch (NameNotFoundException e) {
- packages.remove(a);
- }
- }
- return packages;
- }
-
- /**
- * Called from the backup tasks: record that the given app has been successfully backed up at
- * least once. This includes both key/value and full-data backups through the transport.
- */
- public void logBackupComplete(String packageName) {
- if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
-
- for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
- final Intent notification = new Intent();
- notification.setAction(BACKUP_FINISHED_ACTION);
- notification.setPackage(receiver);
- notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
- | Intent.FLAG_RECEIVER_FOREGROUND);
- notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
- mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
- }
-
- mProcessedPackagesJournal.addPackage(packageName);
- }
-
- /**
- * Persistently record the current and ancestral backup tokens, as well as the set of packages
- * with data available in the ancestral dataset.
- */
- public void writeRestoreTokens() {
- try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
- // First, the version number of this record, for futureproofing
- af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
-
- // Write the ancestral and current tokens
- af.writeLong(mAncestralToken);
- af.writeLong(mCurrentToken);
-
- // Now write the set of ancestral packages
- if (mAncestralPackages == null) {
- af.writeInt(-1);
- } else {
- af.writeInt(mAncestralPackages.size());
- if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
- for (String pkgName : mAncestralPackages) {
- af.writeUTF(pkgName);
- if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
- }
- }
- } catch (IOException e) {
- Slog.w(TAG, "Unable to write token file:", e);
- }
- }
-
- /** Fires off a backup agent, blocking until it attaches or times out. */
- @Nullable
- public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
- IBackupAgent agent = null;
- synchronized (mAgentConnectLock) {
- mConnecting = true;
- mConnectedAgent = null;
- try {
- if (mActivityManager.bindBackupAgent(app.packageName, mode,
- UserHandle.USER_OWNER)) {
- Slog.d(TAG, "awaiting agent for " + app);
-
- // success; wait for the agent to arrive
- // only wait 10 seconds for the bind to happen
- long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
- while (mConnecting && mConnectedAgent == null
- && (System.currentTimeMillis() < timeoutMark)) {
- try {
- mAgentConnectLock.wait(5000);
- } catch (InterruptedException e) {
- // just bail
- Slog.w(TAG, "Interrupted: " + e);
- mConnecting = false;
- mConnectedAgent = null;
- }
- }
-
- // if we timed out with no connect, abort and move on
- if (mConnecting) {
- Slog.w(TAG, "Timeout waiting for agent " + app);
- mConnectedAgent = null;
- }
- if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
- agent = mConnectedAgent;
- }
- } catch (RemoteException e) {
- // can't happen - ActivityManager is local
- }
- }
- if (agent == null) {
- try {
- mActivityManager.clearPendingBackup();
- } catch (RemoteException e) {
- // can't happen - ActivityManager is local
- }
- }
- return agent;
- }
-
- /** Unbind from a backup agent. */
- public void unbindAgent(ApplicationInfo app) {
- try {
- mActivityManager.unbindBackupAgent(app);
- } catch (RemoteException e) {
- // Can't happen - activity manager is local
- }
- }
-
- /**
- * Clear an application's data, blocking until the operation completes or times out. If {@code
- * keepSystemState} is {@code true}, we intentionally do not clear system state that would
- * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
- * bringing it into the actual expected state related to the already-restored notification state
- * etc.
- */
- public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
- // Don't wipe packages marked allowClearUserData=false
- try {
- PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
- if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "allowClearUserData=false so not wiping "
- + packageName);
- }
- return;
- }
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
- return;
- }
-
- ClearDataObserver observer = new ClearDataObserver(this);
-
- synchronized (mClearDataLock) {
- mClearingData = true;
- try {
- mActivityManager.clearApplicationUserData(
- packageName, keepSystemState, observer, 0);
- } catch (RemoteException e) {
- // can't happen because the activity manager is in this process
- }
-
- // only wait 10 seconds for the clear data to happen
- long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
- while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
- try {
- mClearDataLock.wait(5000);
- } catch (InterruptedException e) {
- // won't happen, but still.
- mClearingData = false;
- }
- }
- }
- }
-
- /**
- * Get the restore-set token for the best-available restore set for this {@code packageName}:
- * the active set if possible, else the ancestral one. Returns zero if none available.
- */
- public long getAvailableRestoreToken(String packageName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getAvailableRestoreToken");
-
- long token = mAncestralToken;
- synchronized (mQueueLock) {
- if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "App in ever-stored, so using current token");
- }
- token = mCurrentToken;
- }
- }
- if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
- return token;
- }
-
- /**
- * Requests a backup for the inputted {@code packages}.
- *
- * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
- */
- public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
- return requestBackup(packages, observer, null, flags);
- }
-
- /**
- * Requests a backup for the inputted {@code packages} with a specified {@link
- * IBackupManagerMonitor}.
- */
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
-
- if (packages == null || packages.length < 1) {
- Slog.e(TAG, "No packages named for backup request");
- BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
- BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
- null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
- throw new IllegalArgumentException("No packages are provided for backup");
- }
-
- if (!mEnabled || !mProvisioned) {
- Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
- BackupObserverUtils.sendBackupFinished(observer,
- BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- final int logTag = mProvisioned
- ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
- : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
- return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
- }
-
- final TransportClient transportClient;
- final String transportDirName;
- try {
- transportDirName =
- mTransportManager.getTransportDirName(
- mTransportManager.getCurrentTransportName());
- transportClient =
- mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
- } catch (TransportNotRegisteredException e) {
- BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
- BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
- null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
- return BackupManager.ERROR_TRANSPORT_ABORTED;
- }
-
- OnTaskFinishedListener listener =
- caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
-
- ArrayList<String> fullBackupList = new ArrayList<>();
- ArrayList<String> kvBackupList = new ArrayList<>();
- for (String packageName : packages) {
- if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
- kvBackupList.add(packageName);
- continue;
- }
- try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
- mPackageManager)) {
- BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
- BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- continue;
- }
- if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
- fullBackupList.add(packageInfo.packageName);
- } else {
- kvBackupList.add(packageInfo.packageName);
- }
- } catch (NameNotFoundException e) {
- BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
- BackupManager.ERROR_PACKAGE_NOT_FOUND);
- }
- }
- EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
- fullBackupList.size());
- if (MORE_DEBUG) {
- Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
- + fullBackupList.size() + " full backups, " + kvBackupList.size()
- + " k/v backups");
- }
-
- boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
-
- Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
- msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
- observer, monitor, listener, true, nonIncrementalBackup);
- mBackupHandler.sendMessage(msg);
- return BackupManager.SUCCESS;
- }
-
- /** Cancel all running backups. */
- public void cancelBackups() {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
- if (MORE_DEBUG) {
- Slog.i(TAG, "cancelBackups() called.");
- }
- final long oldToken = Binder.clearCallingIdentity();
- try {
- List<Integer> operationsToCancel = new ArrayList<>();
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- int token = mCurrentOperations.keyAt(i);
- if (op.type == OP_TYPE_BACKUP) {
- operationsToCancel.add(token);
- }
- }
- }
- for (Integer token : operationsToCancel) {
- handleCancel(token, true /* cancelAll */);
- }
- // We don't want the backup jobs to kick in any time soon.
- // Reschedules them to run in the distant future.
- KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
- FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
- } finally {
- Binder.restoreCallingIdentity(oldToken);
- }
- }
-
- /** Schedule a timeout message for the operation identified by {@code token}. */
- public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
- int operationType) {
- if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
- Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
- + Integer.toHexString(token) + " of type " + operationType);
- return;
- }
- if (MORE_DEBUG) {
- Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
- + " interval=" + interval + " callback=" + callback);
- }
-
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
- Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
- token, 0, callback);
- mBackupHandler.sendMessageDelayed(msg, interval);
- }
- }
-
- private int getMessageIdForOperationType(int operationType) {
- switch (operationType) {
- case OP_TYPE_BACKUP_WAIT:
- return MSG_BACKUP_OPERATION_TIMEOUT;
- case OP_TYPE_RESTORE_WAIT:
- return MSG_RESTORE_OPERATION_TIMEOUT;
- default:
- Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
- + operationType);
- return -1;
- }
- }
-
- /**
- * Add an operation to the list of currently running operations. Used for cancellation,
- * completion and timeout callbacks that act on the operation via the {@code token}.
- */
- public void putOperation(int token, Operation operation) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
- + operation.type);
- }
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, operation);
- }
- }
-
- /**
- * Remove an operation from the list of currently running operations. An operation is removed
- * when it is completed, cancelled, or timed out, and thus no longer running.
- */
- public void removeOperation(int token) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
- }
- synchronized (mCurrentOpLock) {
- if (mCurrentOperations.get(token) == null) {
- Slog.w(TAG, "Duplicate remove for operation. token="
- + Integer.toHexString(token));
- }
- mCurrentOperations.remove(token);
- }
- }
-
- /** Block until we received an operation complete message (from the agent or cancellation). */
- public boolean waitUntilOperationComplete(int token) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Blocking until operation complete for "
- + Integer.toHexString(token));
- }
- int finalState = OP_PENDING;
- Operation op = null;
- synchronized (mCurrentOpLock) {
- while (true) {
- op = mCurrentOperations.get(token);
- if (op == null) {
- // mysterious disappearance: treat as success with no callback
- break;
- } else {
- if (op.state == OP_PENDING) {
- try {
- mCurrentOpLock.wait();
- } catch (InterruptedException e) {
- }
- // When the wait is notified we loop around and recheck the current state
- } else {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Unblocked waiting for operation token="
- + Integer.toHexString(token));
- }
- // No longer pending; we're done
- finalState = op.state;
- break;
- }
- }
- }
- }
-
- removeOperation(token);
- if (op != null) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- if (MORE_DEBUG) {
- Slog.v(TAG, "operation " + Integer.toHexString(token)
- + " complete: finalState=" + finalState);
- }
- return finalState == OP_ACKNOWLEDGED;
- }
-
- /** Cancel the operation associated with {@code token}. */
- public void handleCancel(int token, boolean cancelAll) {
- // Notify any synchronous waiters
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (MORE_DEBUG) {
- if (op == null) {
- Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
- + " but no op found");
- }
- }
- int state = (op != null) ? op.state : OP_TIMEOUT;
- if (state == OP_ACKNOWLEDGED) {
- // The operation finished cleanly, so we have nothing more to do.
- if (DEBUG) {
- Slog.w(TAG, "Operation already got an ack."
- + "Should have been removed from mCurrentOperations.");
- }
- op = null;
- mCurrentOperations.delete(token);
- } else if (state == OP_PENDING) {
- if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
- op.state = OP_TIMEOUT;
- // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
- // called after we receive cancel here. We need this op's state there.
-
- // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
- // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
- // doesn't require cancellation.
- if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // If there's a TimeoutHandler for this event, call it
- if (op != null && op.callback != null) {
- if (MORE_DEBUG) {
- Slog.v(TAG, " Invoking cancel on " + op.callback);
- }
- op.callback.handleCancel(cancelAll);
- }
- }
-
- /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
- public boolean isBackupOperationInProgress() {
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
- return true;
- }
- }
- }
- return false;
- }
-
- /** Unbind the backup agent and kill the app if it's a non-system app. */
- public void tearDownAgentAndKill(ApplicationInfo app) {
- if (app == null) {
- // Null means the system package, so just quietly move on. :)
- return;
- }
-
- try {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(app);
-
- // The agent was running with a stub Application object, so shut it down.
- // !!! We hardcode the confirmation UI's package name here rather than use a
- // manifest flag! TODO something less direct.
- if (app.uid >= Process.FIRST_APPLICATION_UID
- && !app.packageName.equals("com.android.backupconfirm")) {
- if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
- mActivityManager.killApplicationProcess(app.processName, app.uid);
- } else {
- if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
- }
- } catch (RemoteException e) {
- Slog.d(TAG, "Lost app trying to shut down");
- }
- }
-
- /** For adb backup/restore. */
- public boolean deviceIsEncrypted() {
- try {
- return mStorageManager.getEncryptionState()
- != StorageManager.ENCRYPTION_STATE_NONE
- && mStorageManager.getPasswordType()
- != StorageManager.CRYPT_TYPE_DEFAULT;
- } catch (Exception e) {
- // If we can't talk to the storagemanager service we have a serious problem; fail
- // "secure" i.e. assuming that the device is encrypted.
- Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
- return true;
- }
- }
-
- // ----- Full-data backup scheduling -----
-
- /**
- * Schedule a job to tell us when it's a good time to run a full backup
- */
- public void scheduleNextFullBackupJob(long transportMinLatency) {
- synchronized (mQueueLock) {
- if (mFullBackupQueue.size() > 0) {
- // schedule the next job at the point in the future when the least-recently
- // backed up app comes due for backup again; or immediately if it's already
- // due.
- final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
- final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
- final long interval = mConstants.getFullBackupIntervalMilliseconds();
- final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
- final long latency = Math.max(transportMinLatency, appLatency);
- Runnable r = new Runnable() {
- @Override
- public void run() {
- FullBackupJob.schedule(mContext, latency, mConstants);
- }
- };
- mBackupHandler.postDelayed(r, 2500);
- } else {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Full backup queue empty; not scheduling");
- }
- }
- }
- }
-
- /**
- * Remove a package from the full-data queue.
- */
- @GuardedBy("mQueueLock")
- private void dequeueFullBackupLocked(String packageName) {
- final int numPackages = mFullBackupQueue.size();
- for (int i = numPackages - 1; i >= 0; i--) {
- final FullBackupEntry e = mFullBackupQueue.get(i);
- if (packageName.equals(e.packageName)) {
- mFullBackupQueue.remove(i);
- }
- }
- }
-
- /**
- * Enqueue full backup for the given app, with a note about when it last ran.
- */
- public void enqueueFullBackup(String packageName, long lastBackedUp) {
- FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
- synchronized (mQueueLock) {
- // First, sanity check that we aren't adding a duplicate. Slow but
- // straightforward; we'll have at most on the order of a few hundred
- // items in this list.
- dequeueFullBackupLocked(packageName);
-
- // This is also slow but easy for modest numbers of apps: work backwards
- // from the end of the queue until we find an item whose last backup
- // time was before this one, then insert this new entry after it. If we're
- // adding something new we don't bother scanning, and just prepend.
- int which = -1;
- if (lastBackedUp > 0) {
- for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
- final FullBackupEntry entry = mFullBackupQueue.get(which);
- if (entry.lastBackup <= lastBackedUp) {
- mFullBackupQueue.add(which + 1, newEntry);
- break;
- }
- }
- }
- if (which < 0) {
- // this one is earlier than any existing one, so prepend
- mFullBackupQueue.add(0, newEntry);
- }
- }
- writeFullBackupScheduleAsync();
- }
-
- private boolean fullBackupAllowable(String transportName) {
- if (!mTransportManager.isTransportRegistered(transportName)) {
- Slog.w(TAG, "Transport not registered; full data backup not performed");
- return false;
- }
-
- // Don't proceed unless we have already established package metadata
- // for the current dataset via a key/value backup pass.
- try {
- String transportDirName = mTransportManager.getTransportDirName(transportName);
- File stateDir = new File(mBaseStateDir, transportDirName);
- File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
- if (pmState.length() <= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Full backup requested but dataset not yet initialized");
- }
- return false;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
- return false;
- }
-
- return true;
- }
-
- /**
- * Conditions are right for a full backup operation, so run one. The model we use is
- * to perform one app backup per scheduled job execution, and to reschedule the job
- * with zero latency as long as conditions remain right and we still have work to do.
- *
- * <p>This is the "start a full backup operation" entry point called by the scheduled job.
- *
- * @return Whether ongoing work will continue. The return value here will be passed
- * along as the return value to the scheduled job's onStartJob() callback.
- */
- public boolean beginFullBackup(FullBackupJob scheduledJob) {
- final long now = System.currentTimeMillis();
- final long fullBackupInterval;
- final long keyValueBackupInterval;
- synchronized (mConstants) {
- fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
- keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
- }
- FullBackupEntry entry = null;
- long latency = fullBackupInterval;
-
- if (!mEnabled || !mProvisioned) {
- // Backups are globally disabled, so don't proceed. We also don't reschedule
- // the job driving automatic backups; that job will be scheduled again when
- // the user enables backup.
- if (MORE_DEBUG) {
- Slog.i(TAG, "beginFullBackup but e=" + mEnabled
- + " p=" + mProvisioned + "; ignoring");
- }
- return false;
- }
-
- // Don't run the backup if we're in battery saver mode, but reschedule
- // to try again in the not-so-distant future.
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
- if (result.batterySaverEnabled) {
- if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
- FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
- return false;
- }
-
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Beginning scheduled full backup operation");
- }
-
- // Great; we're able to run full backup jobs now. See if we have any work to do.
- synchronized (mQueueLock) {
- if (mRunningFullBackupTask != null) {
- Slog.e(TAG, "Backup triggered but one already/still running!");
- return false;
- }
-
- // At this point we think that we have work to do, but possibly not right now.
- // Any exit without actually running backups will also require that we
- // reschedule the job.
- boolean runBackup = true;
- boolean headBusy;
-
- do {
- // Recheck each time, because culling due to ineligibility may
- // have emptied the queue.
- if (mFullBackupQueue.size() == 0) {
- // no work to do so just bow out
- if (DEBUG) {
- Slog.i(TAG, "Backup queue empty; doing nothing");
- }
- runBackup = false;
- break;
- }
-
- headBusy = false;
-
- String transportName = mTransportManager.getCurrentTransportName();
- if (!fullBackupAllowable(transportName)) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Preconditions not met; not running full backup");
- }
- runBackup = false;
- // Typically this means we haven't run a key/value backup yet. Back off
- // full-backup operations by the key/value job's run interval so that
- // next time we run, we are likely to be able to make progress.
- latency = keyValueBackupInterval;
- }
-
- if (runBackup) {
- entry = mFullBackupQueue.get(0);
- long timeSinceRun = now - entry.lastBackup;
- runBackup = (timeSinceRun >= fullBackupInterval);
- if (!runBackup) {
- // It's too early to back up the next thing in the queue, so bow out
- if (MORE_DEBUG) {
- Slog.i(TAG, "Device ready but too early to back up next app");
- }
- // Wait until the next app in the queue falls due for a full data backup
- latency = fullBackupInterval - timeSinceRun;
- break; // we know we aren't doing work yet, so bail.
- }
-
- try {
- PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
- if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
- // The head app isn't supposed to get full-data backups [any more];
- // so we cull it and force a loop around to consider the new head
- // app.
- if (MORE_DEBUG) {
- Slog.i(TAG, "Culling package " + entry.packageName
- + " in full-backup queue but not eligible");
- }
- mFullBackupQueue.remove(0);
- headBusy = true; // force the while() condition
- continue;
- }
-
- final int privFlags = appInfo.applicationInfo.privateFlags;
- headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
- && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
-
- if (headBusy) {
- final long nextEligible = System.currentTimeMillis()
- + BUSY_BACKOFF_MIN_MILLIS
- + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
- if (DEBUG_SCHEDULING) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Slog.i(TAG, "Full backup time but " + entry.packageName
- + " is busy; deferring to "
- + sdf.format(new Date(nextEligible)));
- }
- // This relocates the app's entry from the head of the queue to
- // its order-appropriate position further down, so upon looping
- // a new candidate will be considered at the head.
- enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
- }
- } catch (NameNotFoundException nnf) {
- // So, we think we want to back this up, but it turns out the package
- // in question is no longer installed. We want to drop it from the
- // queue entirely and move on, but if there's nothing else in the queue
- // we should bail entirely. headBusy cannot have been set to true yet.
- runBackup = (mFullBackupQueue.size() > 1);
- } catch (RemoteException e) {
- // Cannot happen; the Activity Manager is in the same process
- }
- }
- } while (headBusy);
-
- if (!runBackup) {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
- }
- final long deferTime = latency; // pin for the closure
- mBackupHandler.post(new Runnable() {
- @Override
- public void run() {
- FullBackupJob.schedule(mContext, deferTime, mConstants);
- }
- });
- return false;
- }
-
- // Okay, the top thing is ready for backup now. Do it.
- mFullBackupQueue.remove(0);
- CountDownLatch latch = new CountDownLatch(1);
- String[] pkg = new String[]{entry.packageName};
- mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- /* observer */ null,
- pkg,
- /* updateSchedule */ true,
- scheduledJob,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.beginFullBackup()");
- // Acquiring wakelock for PerformFullTransportBackupTask before its start.
- mWakelock.acquire();
- (new Thread(mRunningFullBackupTask)).start();
- }
-
- return true;
- }
-
- /**
- * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
- * task right away.
- */
- public void endFullBackup() {
- // offload the mRunningFullBackupTask.handleCancel() call to another thread,
- // as we might have to wait for mCancelLock
- Runnable endFullBackupRunnable = new Runnable() {
- @Override
- public void run() {
- PerformFullTransportBackupTask pftbt = null;
- synchronized (mQueueLock) {
- if (mRunningFullBackupTask != null) {
- pftbt = mRunningFullBackupTask;
- }
- }
- if (pftbt != null) {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Telling running backup to stop");
- }
- pftbt.handleCancel(true);
- }
- }
- };
- new Thread(endFullBackupRunnable, "end-full-backup").start();
- }
-
- /** Used by both incremental and full restore to restore widget data. */
- public void restoreWidgetData(String packageName, byte[] widgetData) {
- // Apply the restored widget state and generate the ID update for the app
- // TODO: http://b/22388012
- if (MORE_DEBUG) {
- Slog.i(TAG, "Incorporating restored widget data");
- }
- AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
- }
-
- // *****************************
- // NEW UNIFIED RESTORE IMPLEMENTATION
- // *****************************
-
- /** Schedule a backup pass for {@code packageName}. */
- public void dataChangedImpl(String packageName) {
- HashSet<String> targets = dataChangedTargets(packageName);
- dataChangedImpl(packageName, targets);
- }
-
- private void dataChangedImpl(String packageName, HashSet<String> targets) {
- // Record that we need a backup pass for the caller. Since multiple callers
- // may share a uid, we need to note all candidates within that uid and schedule
- // a backup pass for each of them.
- if (targets == null) {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- // Note that this client has made data changes that need to be backed up
- if (targets.contains(packageName)) {
- // Add the caller to the set of pending backups. If there is
- // one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(packageName);
- if (mPendingBackups.put(packageName, req) == null) {
- if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
-
- // Journal this request in case of crash. The put()
- // operation returned null when this package was not already
- // in the set; we want to avoid touching the disk redundantly.
- writeToJournalLocked(packageName);
- }
- }
- }
-
- // ...and schedule a backup pass if necessary
- KeyValueBackupJob.schedule(mContext, mConstants);
- }
-
- // Note: packageName is currently unused, but may be in the future
- private HashSet<String> dataChangedTargets(String packageName) {
- // If the caller does not hold the BACKUP permission, it can only request a
- // backup of its own data.
- if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
- Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
- synchronized (mBackupParticipants) {
- return mBackupParticipants.get(Binder.getCallingUid());
- }
- }
-
- // a caller with full permission can ask to back up any participating app
- if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
- return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
- } else {
- synchronized (mBackupParticipants) {
- return SparseArrayUtils.union(mBackupParticipants);
- }
- }
- }
-
- private void writeToJournalLocked(String str) {
- try {
- if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
- mJournal.addPackage(str);
- } catch (IOException e) {
- Slog.e(TAG, "Can't write " + str + " to backup journal", e);
- mJournal = null;
- }
- }
-
- // ----- IBackupManager binder interface -----
-
- /** Sent from an app's backup agent to let the service know that there's new data to backup. */
- public void dataChanged(final String packageName) {
- final int callingUserHandle = UserHandle.getCallingUserId();
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- // TODO: http://b/22388012
- // App is running under a non-owner user profile. For now, we do not back
- // up data from secondary user profiles.
- // TODO: backups for all user profiles although don't add backup for profiles
- // without adding admin control in DevicePolicyManager.
- if (MORE_DEBUG) {
- Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
- + callingUserHandle);
- }
- return;
- }
-
- final HashSet<String> targets = dataChangedTargets(packageName);
- if (targets == null) {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
- return;
- }
-
- mBackupHandler.post(new Runnable() {
- public void run() {
- dataChangedImpl(packageName, targets);
- }
- });
- }
-
- /** Run an initialize operation for the given transport. */
+ /** Run an initialize operation for the given transports {@code transportNames}. */
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "initializeTransport");
- Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- mWakelock.acquire();
- OnTaskFinishedListener listener = caller -> mWakelock.release();
- mBackupHandler.post(
- new PerformInitializeTask(this, transportNames, observer, listener));
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ mUserBackupManagerService.initializeTransports(transportNames, observer);
}
- /** Clear the given package's backup data from the current transport. */
+ /**
+ * Clear the given package {@code packageName}'s backup data from the transport {@code
+ * transportName}.
+ */
public void clearBackupData(String transportName, String packageName) {
- if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
- PackageInfo info;
- try {
- info = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
- } catch (NameNotFoundException e) {
- Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
- return;
- }
-
- // If the caller does not hold the BACKUP permission, it can only request a
- // wipe of its own backed-up data.
- Set<String> apps;
- if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
- Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
- apps = mBackupParticipants.get(Binder.getCallingUid());
- } else {
- // a caller with full permission can ask to back up any participating app
- // !!! TODO: allow data-clear of ANY app?
- if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
- apps = mProcessedPackagesJournal.getPackagesCopy();
- }
-
- if (apps.contains(packageName)) {
- // found it; fire off the clear request
- if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
- mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
- synchronized (mQueueLock) {
- TransportClient transportClient =
- mTransportManager
- .getTransportClient(transportName, "BMS.clearBackupData()");
- if (transportClient == null) {
- // transport is currently unregistered -- make sure to retry
- Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
- new ClearRetryParams(transportName, packageName));
- mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
- return;
- }
- long oldId = Binder.clearCallingIdentity();
- OnTaskFinishedListener listener =
- caller ->
- mTransportManager.disposeOfTransportClient(transportClient, caller);
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(
- MSG_RUN_CLEAR,
- new ClearParams(transportClient, info, listener));
- mBackupHandler.sendMessage(msg);
- Binder.restoreCallingIdentity(oldId);
- }
- }
+ mUserBackupManagerService.clearBackupData(transportName, packageName);
}
- /**
- * Run a backup pass immediately for any applications that have declared that they have pending
- * updates.
- */
- public void backupNow() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
-
- long oldId = Binder.clearCallingIdentity();
- try {
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
- if (result.batterySaverEnabled) {
- if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
- KeyValueBackupJob.schedule(mContext, mConstants); // try again in several hours
- } else {
- if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
- synchronized (mQueueLock) {
- // Fire the intent that kicks off the whole shebang...
- try {
- mRunBackupIntent.send();
- } catch (PendingIntent.CanceledException e) {
- // should never happen
- Slog.e(TAG, "run-backup intent cancelled!");
- }
-
- // ...and cancel any pending scheduled job, because we've just superseded it
- KeyValueBackupJob.cancel(mContext);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** Returns {@code true} if the system user has gone through SUW. */
- public boolean deviceIsProvisioned() {
- final ContentResolver resolver = mContext.getContentResolver();
- return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
- }
-
- /**
- * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
- * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
- * return to the caller until the backup has been completed. It requires on-screen confirmation
- * by the user.
- */
- public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
- boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
- boolean compress, boolean doKeyValue, String[] pkgList) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
-
- final int callingUserHandle = UserHandle.getCallingUserId();
- // TODO: http://b/22388012
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- throw new IllegalStateException("Backup supported only for the device owner");
- }
-
- // Validate
- if (!doAllApps) {
- if (!includeShared) {
- // If we're backing up shared data (sdcard or equivalent), then we can run
- // without any supplied app names. Otherwise, we'd be doing no work, so
- // report the error.
- if (pkgList == null || pkgList.length == 0) {
- throw new IllegalArgumentException(
- "Backup requested but neither shared nor any apps named");
- }
- }
- }
-
- long oldId = Binder.clearCallingIdentity();
- try {
- // Doesn't make sense to do a full backup prior to setup
- if (!deviceIsProvisioned()) {
- Slog.i(TAG, "Backup not supported before setup");
- return;
- }
-
- if (DEBUG) {
- Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
- + " shared=" + includeShared + " all=" + doAllApps + " system="
- + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
- }
- Slog.i(TAG, "Beginning adb backup...");
-
- AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
- includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
- pkgList);
- final int token = generateRandomIntegerToken();
- synchronized (mAdbBackupRestoreConfirmations) {
- mAdbBackupRestoreConfirmations.put(token, params);
- }
-
- // start up the confirmation UI
- if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
- if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
- Slog.e(TAG, "Unable to launch backup confirmation UI");
- mAdbBackupRestoreConfirmations.delete(token);
- return;
- }
-
- // make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER,
- 0);
-
- // start the confirmation countdown
- startConfirmationTimeout(token, params);
-
- // wait for the backup to be performed
- if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
- waitForCompletion(params);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
- }
- Binder.restoreCallingIdentity(oldId);
- Slog.d(TAG, "Adb backup processing complete.");
- }
- }
-
- /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
- public void fullTransportBackup(String[] pkgNames) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "fullTransportBackup");
-
- final int callingUserHandle = UserHandle.getCallingUserId();
- // TODO: http://b/22388012
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- throw new IllegalStateException("Restore supported only for the device owner");
- }
-
- String transportName = mTransportManager.getCurrentTransportName();
- if (!fullBackupAllowable(transportName)) {
- Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
- } else {
- if (DEBUG) {
- Slog.d(TAG, "fullTransportBackup()");
- }
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- CountDownLatch latch = new CountDownLatch(1);
- Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- /* observer */ null,
- pkgNames,
- /* updateSchedule */ false,
- /* runningJob */ null,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.fullTransportBackup()");
- // Acquiring wakelock for PerformFullTransportBackupTask before its start.
- mWakelock.acquire();
- (new Thread(task, "full-transport-master")).start();
- do {
- try {
- latch.await();
- break;
- } catch (InterruptedException e) {
- // Just go back to waiting for the latch to indicate completion
- }
- } while (true);
-
- // We just ran a backup on these packages, so kick them to the end of the queue
- final long now = System.currentTimeMillis();
- for (String pkg : pkgNames) {
- enqueueFullBackup(pkg, now);
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Done with full transport backup.");
- }
- }
-
- /**
- * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
- * confirmation.
- */
- public void adbRestore(ParcelFileDescriptor fd) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
-
- final int callingUserHandle = UserHandle.getCallingUserId();
- // TODO: http://b/22388012
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- throw new IllegalStateException("Restore supported only for the device owner");
- }
-
- long oldId = Binder.clearCallingIdentity();
-
- try {
- // Check whether the device has been provisioned -- we don't handle
- // full restores prior to completing the setup process.
- if (!deviceIsProvisioned()) {
- Slog.i(TAG, "Full restore not permitted before setup");
- return;
- }
-
- Slog.i(TAG, "Beginning restore...");
-
- AdbRestoreParams params = new AdbRestoreParams(fd);
- final int token = generateRandomIntegerToken();
- synchronized (mAdbBackupRestoreConfirmations) {
- mAdbBackupRestoreConfirmations.put(token, params);
- }
-
- // start up the confirmation UI
- if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
- if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
- Slog.e(TAG, "Unable to launch restore confirmation");
- mAdbBackupRestoreConfirmations.delete(token);
- return;
- }
-
- // make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER,
- 0);
-
- // start the confirmation countdown
- startConfirmationTimeout(token, params);
-
- // wait for the restore to be performed
- if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
- waitForCompletion(params);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
- }
- Binder.restoreCallingIdentity(oldId);
- Slog.i(TAG, "adb restore processing complete.");
- }
- }
-
- private boolean startConfirmationUi(int token, String action) {
- try {
- Intent confIntent = new Intent(action);
- confIntent.setClassName("com.android.backupconfirm",
- "com.android.backupconfirm.BackupRestoreConfirmation");
- confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
- confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
- } catch (ActivityNotFoundException e) {
- return false;
- }
- return true;
- }
-
- private void startConfirmationTimeout(int token, AdbParams params) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Posting conf timeout msg after "
- + TIMEOUT_FULL_CONFIRMATION + " millis");
- }
- Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
- token, 0, params);
- mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
- }
-
- private void waitForCompletion(AdbParams params) {
- synchronized (params.latch) {
- while (!params.latch.get()) {
- try {
- params.latch.wait();
- } catch (InterruptedException e) { /* never interrupted */ }
- }
- }
- }
-
- /** Called when adb backup/restore has completed. */
- public void signalAdbBackupRestoreCompletion(AdbParams params) {
- synchronized (params.latch) {
- params.latch.set(true);
- params.latch.notifyAll();
- }
- }
-
- /**
- * Confirm that the previously-requested full backup/restore operation can proceed. This is used
- * to require a user-facing disclosure about the operation.
- */
- public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
- String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
- if (DEBUG) {
- Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
- + " allow=" + allow);
- }
-
- // TODO: possibly require not just this signature-only permission, but even
- // require that the specific designated confirmation-UI app uid is the caller?
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "acknowledgeAdbBackupOrRestore");
-
- long oldId = Binder.clearCallingIdentity();
- try {
-
- AdbParams params;
- synchronized (mAdbBackupRestoreConfirmations) {
- params = mAdbBackupRestoreConfirmations.get(token);
- if (params != null) {
- mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
- mAdbBackupRestoreConfirmations.delete(token);
-
- if (allow) {
- final int verb = params instanceof AdbBackupParams
- ? MSG_RUN_ADB_BACKUP
- : MSG_RUN_ADB_RESTORE;
-
- params.observer = observer;
- params.curPassword = curPassword;
-
- params.encryptPassword = encPpassword;
-
- if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(verb, params);
- mBackupHandler.sendMessage(msg);
- } else {
- Slog.w(TAG, "User rejected full backup/restore operation");
- // indicate completion without having actually transferred any data
- signalAdbBackupRestoreCompletion(params);
- }
- } else {
- Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
- }
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** User-configurable enabling/disabling of backups. */
- public void setBackupEnabled(boolean enable) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupEnabled");
-
- Slog.i(TAG, "Backup enabled => " + enable);
-
- long oldId = Binder.clearCallingIdentity();
- try {
- boolean wasEnabled = mEnabled;
- synchronized (this) {
- writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
- mEnabled = enable;
- }
-
- synchronized (mQueueLock) {
- if (enable && !wasEnabled && mProvisioned) {
- // if we've just been enabled, start scheduling backup passes
- KeyValueBackupJob.schedule(mContext, mConstants);
- scheduleNextFullBackupJob(0);
- } else if (!enable) {
- // No longer enabled, so stop running backups
- if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
-
- KeyValueBackupJob.cancel(mContext);
-
- // This also constitutes an opt-out, so we wipe any data for
- // this device from the backend. We start that process with
- // an alarm in order to guarantee wakelock states.
- if (wasEnabled && mProvisioned) {
- // NOTE: we currently flush every registered transport, not just
- // the currently-active one.
- List<String> transportNames = new ArrayList<>();
- List<String> transportDirNames = new ArrayList<>();
- mTransportManager.forEachRegisteredTransport(
- name -> {
- final String dirName;
- try {
- dirName =
- mTransportManager
- .getTransportDirName(name);
- } catch (TransportNotRegisteredException e) {
- // Should never happen
- Slog.e(TAG, "Unexpected unregistered transport", e);
- return;
- }
- transportNames.add(name);
- transportDirNames.add(dirName);
- });
-
- // build the set of transports for which we are posting an init
- for (int i = 0; i < transportNames.size(); i++) {
- recordInitPending(
- true,
- transportNames.get(i),
- transportDirNames.get(i));
- }
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
- mRunInitIntent);
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** Enable/disable automatic restore of app data at install time. */
- public void setAutoRestore(boolean doAutoRestore) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setAutoRestore");
-
- Slog.i(TAG, "Auto restore => " + doAutoRestore);
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
- mAutoRestore = doAutoRestore;
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** Mark the backup service as having been provisioned. */
- public void setBackupProvisioned(boolean available) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupProvisioned");
- /*
- * This is now a no-op; provisioning is simply the device's own setup state.
- */
- }
-
- /** Report whether the backup mechanism is currently enabled. */
- public boolean isBackupEnabled() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "isBackupEnabled");
- return mEnabled; // no need to synchronize just to read it
- }
-
- /** Report the name of the currently active transport. */
+ /** Return the name of the currently active transport. */
public String getCurrentTransport() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getCurrentTransport");
- String currentTransport = mTransportManager.getCurrentTransportName();
- if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
- return currentTransport;
+ return mUserBackupManagerService.getCurrentTransport();
}
/**
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered.
*/
- @Nullable
public ComponentName getCurrentTransportComponent() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
- long oldId = Binder.clearCallingIdentity();
- try {
- return mTransportManager.getCurrentTransportComponent();
- } catch (TransportNotRegisteredException e) {
- return null;
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ return mUserBackupManagerService.getCurrentTransportComponent();
}
/** Report all known, available backup transports by name. */
public String[] listAllTransports() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "listAllTransports");
-
- return mTransportManager.getRegisteredTransportNames();
+ return mUserBackupManagerService.listAllTransports();
}
- /** Report all known, available backup transports by component. */
+ /** Report all known, available backup transports by {@link ComponentName}. */
public ComponentName[] listAllTransportComponents() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "listAllTransportComponents");
- return mTransportManager.getRegisteredTransportComponents();
+ return mUserBackupManagerService.listAllTransportComponents();
}
/** Report all system whitelisted transports. */
public String[] getTransportWhitelist() {
- // No permission check, intentionally.
- Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
- String[] whitelistedTransports = new String[whitelistedComponents.size()];
- int i = 0;
- for (ComponentName component : whitelistedComponents) {
- whitelistedTransports[i] = component.flattenToShortString();
- i++;
- }
- return whitelistedTransports;
+ return mUserBackupManagerService.getTransportWhitelist();
}
/**
@@ -2916,18 +282,17 @@
* @param transportComponent The identity of the transport being described.
* @param name A {@link String} with the new name for the transport. This is NOT for
* identification. MUST NOT be {@code null}.
- * @param configurationIntent An {@link Intent} that can be passed to
- * {@link Context#startActivity} in order to launch the transport's configuration UI. It may
- * be {@code null} if the transport does not offer any user-facing configuration UI.
+ * @param configurationIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's configuration UI. It may be
+ * {@code null} if the transport does not offer any user-facing configuration UI.
* @param currentDestinationString A {@link String} describing the destination to which the
* transport is currently sending data. MUST NOT be {@code null}.
- * @param dataManagementIntent An {@link Intent} that can be passed to
- * {@link Context#startActivity} in order to launch the transport's data-management UI. It
- * may be {@code null} if the transport does not offer any user-facing data
- * management UI.
+ * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's data-management UI. It may be
+ * {@code null} if the transport does not offer any user-facing data management UI.
* @param dataManagementLabel A {@link String} to be used as the label for the transport's data
- * management affordance. This MUST be {@code null} when dataManagementIntent is
- * {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+ * management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+ * null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
* @throws SecurityException If the UID of the calling process differs from the package UID of
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
@@ -2937,9 +302,8 @@
@Nullable Intent configurationIntent,
String currentDestinationString,
@Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- updateTransportAttributes(
- Binder.getCallingUid(),
+ String dataManagementLabel) {
+ mUserBackupManagerService.updateTransportAttributes(
transportComponent,
name,
configurationIntent,
@@ -2948,73 +312,16 @@
dataManagementLabel);
}
- @VisibleForTesting
- void updateTransportAttributes(
- int callingUid,
- ComponentName transportComponent,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "updateTransportAttributes");
-
- Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
- Preconditions.checkNotNull(name, "name can't be null");
- Preconditions.checkNotNull(
- currentDestinationString, "currentDestinationString can't be null");
- Preconditions.checkArgument(
- (dataManagementIntent == null) == (dataManagementLabel == null),
- "dataManagementLabel should be null iff dataManagementIntent is null");
-
- try {
- int transportUid =
- mContext.getPackageManager()
- .getPackageUid(transportComponent.getPackageName(), 0);
- if (callingUid != transportUid) {
- throw new SecurityException("Only the transport can change its description");
- }
- } catch (NameNotFoundException e) {
- throw new SecurityException("Transport package not found", e);
- }
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- mTransportManager.updateTransportAttributes(
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
/**
- * Selects transport {@code transportName} and returns previously selected transport.
+ * Selects transport {@code transportName} and returns the previously selected transport.
*
* @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} instead.
+ * ISelectBackupTransportCallback)} instead.
*/
@Deprecated
@Nullable
public String selectBackupTransport(String transportName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "selectBackupTransport");
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- String previousTransportName = mTransportManager.selectTransport(transportName);
- updateStateForTransport(transportName);
- Slog.v(TAG, "selectBackupTransport(transport = " + transportName
- + "): previous transport = " + previousTransportName);
- return previousTransportName;
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ return mUserBackupManagerService.selectBackupTransport(transportName);
}
/**
@@ -3023,70 +330,7 @@
*/
public void selectBackupTransportAsync(
ComponentName transportComponent, ISelectBackupTransportCallback listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- String transportString = transportComponent.flattenToShortString();
- Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
- mBackupHandler.post(
- () -> {
- String transportName = null;
- int result =
- mTransportManager.registerAndSelectTransport(transportComponent);
- if (result == BackupManager.SUCCESS) {
- try {
- transportName =
- mTransportManager.getTransportName(transportComponent);
- updateStateForTransport(transportName);
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Transport got unregistered");
- result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
- }
- }
-
- try {
- if (transportName != null) {
- listener.onSuccess(transportName);
- } else {
- listener.onFailure(result);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
- }
- });
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- private void updateStateForTransport(String newTransportName) {
- // Publish the name change
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT, newTransportName);
-
- // And update our current-dataset bookkeeping
- String callerLogString = "BMS.updateStateForTransport()";
- TransportClient transportClient =
- mTransportManager.getTransportClient(newTransportName, callerLogString);
- if (transportClient != null) {
- try {
- IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
- mCurrentToken = transport.getCurrentRestoreSet();
- } catch (Exception e) {
- // Oops. We can't know the current dataset token, so reset and figure it out
- // when we do the next k/v backup operation on this transport.
- mCurrentToken = 0;
- Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
- }
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- } else {
- Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
- // The named transport isn't registered, so we can't know what its current dataset token
- // is. Reset as above.
- mCurrentToken = 0;
- }
+ mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
}
/**
@@ -3095,18 +339,7 @@
* returns {@code null}.
*/
public Intent getConfigurationIntent(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getConfigurationIntent");
- try {
- Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
- }
- return intent;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
- return null;
- }
+ return mUserBackupManagerService.getConfigurationIntent(transportName);
}
/**
@@ -3119,36 +352,12 @@
* @return The current destination string or null if the transport is not registered.
*/
public String getDestinationString(String transportName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "getDestinationString");
-
- try {
- String string = mTransportManager.getTransportCurrentDestinationString(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDestinationString() returning " + string);
- }
- return string;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
- return null;
- }
+ return mUserBackupManagerService.getDestinationString(transportName);
}
/** Supply the manage-data intent for the given transport. */
public Intent getDataManagementIntent(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getDataManagementIntent");
-
- try {
- Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
- }
- return intent;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
- return null;
- }
+ return mUserBackupManagerService.getDataManagementIntent(transportName);
}
/**
@@ -3156,443 +365,202 @@
* transport.
*/
public String getDataManagementLabel(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getDataManagementLabel");
+ return mUserBackupManagerService.getDataManagementLabel(transportName);
+ }
- try {
- String label = mTransportManager.getTransportDataManagementLabel(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementLabel() returning " + label);
- }
- return label;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
- return null;
- }
+ // ---------------------------------------------
+ // SETTINGS OPERATIONS
+ // ---------------------------------------------
+
+ /** Enable/disable the backup service. This is user-configurable via backup settings. */
+ public void setBackupEnabled(boolean enable) {
+ mUserBackupManagerService.setBackupEnabled(enable);
+ }
+
+ /** Enable/disable automatic restore of app data at install time. */
+ public void setAutoRestore(boolean autoRestore) {
+ mUserBackupManagerService.setAutoRestore(autoRestore);
+ }
+
+ /** Mark the backup service as having been provisioned (device has gone through SUW). */
+ public void setBackupProvisioned(boolean provisioned) {
+ mUserBackupManagerService.setBackupProvisioned(provisioned);
}
/**
- * Callback: a requested backup agent has been instantiated. This should only be called from the
- * {@link ActivityManager}.
+ * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
- public void agentConnected(String packageName, IBinder agentBinder) {
- synchronized (mAgentConnectLock) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
- IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
- mConnectedAgent = agent;
- mConnecting = false;
- } else {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " claiming agent connected");
- }
- mAgentConnectLock.notifyAll();
- }
+ public boolean isBackupEnabled() {
+ return mUserBackupManagerService.isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // BACKUP OPERATIONS
+ // ---------------------------------------------
+
+ /** Checks if the given package {@code packageName} is eligible for backup. */
+ public boolean isAppEligibleForBackup(String packageName) {
+ return mUserBackupManagerService.isAppEligibleForBackup(packageName);
}
/**
- * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
- * to come up in the first place, the agentBinder argument will be {@code null}. This should
- * only be called from the {@link ActivityManager}.
+ * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
- public void agentDisconnected(String packageName) {
- // TODO: handle backup being interrupted
- synchronized (mAgentConnectLock) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- mConnectedAgent = null;
- mConnecting = false;
- } else {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " claiming agent disconnected");
- }
- mAgentConnectLock.notifyAll();
- }
+ public String[] filterAppsEligibleForBackup(String[] packages) {
+ return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
}
/**
- * An application being installed will need a restore pass, then the {@link PackageManager} will
- * need to be told when the restore is finished.
+ * Run a backup pass immediately for any key-value backup applications that have declared that
+ * they have pending updates.
+ */
+ public void backupNow() {
+ mUserBackupManagerService.backupNow();
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages} with a specified callback {@link
+ * IBackupManagerMonitor} for receiving events during the operation.
+ */
+ public int requestBackup(
+ String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+ return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ }
+
+ /** Cancel all running backup operations. */
+ public void cancelBackups() {
+ mUserBackupManagerService.cancelBackups();
+ }
+
+ /**
+ * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+ * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+ * zero latency as long as conditions remain right and we still have work to do.
+ *
+ * @return Whether ongoing work will continue. The return value here will be passed along as the
+ * return value to the callback {@link JobService#onStartJob(JobParameters)}.
+ */
+ public boolean beginFullBackup(FullBackupJob scheduledJob) {
+ return mUserBackupManagerService.beginFullBackup(scheduledJob);
+ }
+
+ /**
+ * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+ * longer met for running the full backup job.
+ */
+ public void endFullBackup() {
+ mUserBackupManagerService.endFullBackup();
+ }
+
+ /**
+ * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+ */
+ public void fullTransportBackup(String[] packageNames) {
+ mUserBackupManagerService.fullTransportBackup(packageNames);
+ }
+
+ // ---------------------------------------------
+ // RESTORE OPERATIONS
+ // ---------------------------------------------
+
+ /**
+ * Used to run a restore pass for an application that is being installed. This should only be
+ * called from the {@link PackageManager}.
*/
public void restoreAtInstall(String packageName, int token) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " attemping install-time restore");
- return;
- }
-
- boolean skip = false;
-
- long restoreSet = getAvailableRestoreToken(packageName);
- if (DEBUG) {
- Slog.v(TAG, "restoreAtInstall pkg=" + packageName
- + " token=" + Integer.toHexString(token)
- + " restoreSet=" + Long.toHexString(restoreSet));
- }
- if (restoreSet == 0) {
- if (MORE_DEBUG) Slog.i(TAG, "No restore set");
- skip = true;
- }
-
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
- if (transportClient == null) {
- if (DEBUG) Slog.w(TAG, "No transport client");
- skip = true;
- }
-
- if (!mAutoRestore) {
- if (DEBUG) {
- Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
- }
- skip = true;
- }
-
- if (!skip) {
- try {
- // okay, we're going to attempt a restore of this package from this restore set.
- // The eventual message back into the Package Manager to run the post-install
- // steps for 'token' will be issued from the restore handling code.
-
- mWakelock.acquire();
-
- OnTaskFinishedListener listener = caller -> {
- mTransportManager.disposeOfTransportClient(transportClient, caller);
- mWakelock.release();
- };
-
- if (MORE_DEBUG) {
- Slog.d(TAG, "Restore at install of " + packageName);
- }
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj =
- RestoreParams.createForRestoreAtInstall(
- transportClient,
- /* observer */ null,
- /* monitor */ null,
- restoreSet,
- packageName,
- token,
- listener);
- mBackupHandler.sendMessage(msg);
- } catch (Exception e) {
- // Calling into the transport broke; back off and proceed with the installation.
- Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
- skip = true;
- }
- }
-
- if (skip) {
- // Auto-restore disabled or no way to attempt a restore
-
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(
- transportClient, "BMS.restoreAtInstall()");
- }
-
- // Tell the PackageManager to proceed with the post-install handling for this package.
- if (DEBUG) Slog.v(TAG, "Finishing install immediately");
- try {
- mPackageManagerBinder.finishPackageInstall(token, false);
- } catch (RemoteException e) { /* can't happen */ }
- }
- }
-
- /** Hand off a restore session. */
- public IRestoreSession beginRestoreSession(String packageName, String transport) {
- if (DEBUG) {
- Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
- + " transport=" + transport);
- }
-
- boolean needPermission = true;
- if (transport == null) {
- transport = mTransportManager.getCurrentTransportName();
-
- if (packageName != null) {
- PackageInfo app = null;
- try {
- app = mPackageManager.getPackageInfo(packageName, 0);
- } catch (NameNotFoundException nnf) {
- Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
- throw new IllegalArgumentException("Package " + packageName + " not found");
- }
-
- if (app.applicationInfo.uid == Binder.getCallingUid()) {
- // So: using the current active transport, and the caller has asked
- // that its own package will be restored. In this narrow use case
- // we do not require the caller to hold the permission.
- needPermission = false;
- }
- }
- }
-
- if (needPermission) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "beginRestoreSession");
- } else {
- if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
- }
-
- synchronized (this) {
- if (mActiveRestoreSession != null) {
- Slog.i(TAG, "Restore session requested but one already active");
- return null;
- }
- if (mBackupRunning) {
- Slog.i(TAG, "Restore session requested but currently running backups");
- return null;
- }
- mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
- mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
- mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
- }
- return mActiveRestoreSession;
- }
-
- /** Clear the specified restore session. */
- public void clearRestoreSession(ActiveRestoreSession currentSession) {
- synchronized (this) {
- if (currentSession != mActiveRestoreSession) {
- Slog.e(TAG, "ending non-current restore session");
- } else {
- if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
- mActiveRestoreSession = null;
- mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- }
- }
+ mUserBackupManagerService.restoreAtInstall(packageName, token);
}
/**
- * Note that a currently-active backup agent has notified us that it has completed the given
- * outstanding asynchronous backup/restore operation.
+ * Begin a restore for the specified package {@code packageName} using the specified transport
+ * {@code transportName}.
*/
- public void opComplete(int token, long result) {
- if (MORE_DEBUG) {
- Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
- }
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (op != null) {
- if (op.state == OP_TIMEOUT) {
- // The operation already timed out, and this is a late response. Tidy up
- // and ignore it; we've already dealt with the timeout.
- op = null;
- mCurrentOperations.delete(token);
- } else if (op.state == OP_ACKNOWLEDGED) {
- if (DEBUG) {
- Slog.w(TAG, "Received duplicate ack for token="
- + Integer.toHexString(token));
- }
- op = null;
- mCurrentOperations.remove(token);
- } else if (op.state == OP_PENDING) {
- // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
- // called after we we receive this call.
- op.state = OP_ACKNOWLEDGED;
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // The completion callback, if any, is invoked on the handler
- if (op != null && op.callback != null) {
- Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
- Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
- mBackupHandler.sendMessage(msg);
- }
+ public IRestoreSession beginRestoreSession(String packageName, String transportName) {
+ return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
}
- /** Checks if the package is eligible for backup. */
- public boolean isAppEligibleForBackup(String packageName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
-
- long oldToken = Binder.clearCallingIdentity();
- try {
- String callerLogString = "BMS.isAppEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- boolean eligible =
- AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager);
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- }
- return eligible;
- } finally {
- Binder.restoreCallingIdentity(oldToken);
- }
+ /**
+ * Get the restore-set token for the best-available restore set for this {@code packageName}:
+ * the active set if possible, else the ancestral one. Returns zero if none available.
+ */
+ public long getAvailableRestoreToken(String packageName) {
+ return mUserBackupManagerService.getAvailableRestoreToken(packageName);
}
- /** Returns the inputted packages that are eligible for backup. */
- public String[] filterAppsEligibleForBackup(String[] packages) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+ // ---------------------------------------------
+ // ADB BACKUP/RESTORE OPERATIONS
+ // ---------------------------------------------
- long oldToken = Binder.clearCallingIdentity();
- try {
- String callerLogString = "BMS.filterAppsEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- List<String> eligibleApps = new LinkedList<>();
- for (String packageName : packages) {
- if (AppBackupUtils
- .appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager)) {
- eligibleApps.add(packageName);
- }
- }
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- }
- return eligibleApps.toArray(new String[eligibleApps.size()]);
- } finally {
- Binder.restoreCallingIdentity(oldToken);
- }
+ /** Sets the backup password used when running adb backup. */
+ public boolean setBackupPassword(String currentPassword, String newPassword) {
+ return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
}
+ /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+ public boolean hasBackupPassword() {
+ return mUserBackupManagerService.hasBackupPassword();
+ }
+
+ /**
+ * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
+ * command line, writing the resulting data stream to the supplied {@code fd}. This method is
+ * synchronous and does not return to the caller until the backup has been completed. It
+ * requires on-screen confirmation by the user.
+ */
+ public void adbBackup(
+ ParcelFileDescriptor fd,
+ boolean includeApks,
+ boolean includeObbs,
+ boolean includeShared,
+ boolean doWidgets,
+ boolean doAllApps,
+ boolean includeSystem,
+ boolean doCompress,
+ boolean doKeyValue,
+ String[] packageNames) {
+ mUserBackupManagerService.adbBackup(
+ fd,
+ includeApks,
+ includeObbs,
+ includeShared,
+ doWidgets,
+ doAllApps,
+ includeSystem,
+ doCompress,
+ doKeyValue,
+ packageNames);
+ }
+
+ /**
+ * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
+ * is synchronous and does not return to the caller until the restore has been completed. It
+ * requires on-screen confirmation by the user.
+ */
+ public void adbRestore(ParcelFileDescriptor fd) {
+ mUserBackupManagerService.adbRestore(fd);
+ }
+
+ /**
+ * Confirm that the previously requested adb backup/restore operation can proceed. This is used
+ * to require a user-facing disclosure about the operation.
+ */
+ public void acknowledgeAdbBackupOrRestore(
+ int token,
+ boolean allow,
+ String currentPassword,
+ String encryptionPassword,
+ IFullBackupRestoreObserver observer) {
+ mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
+ token, allow, currentPassword, encryptionPassword, observer);
+ }
+
+ // ---------------------------------------------
+ // SERVICE OPERATIONS
+ // ---------------------------------------------
+
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
-
- long identityToken = Binder.clearCallingIdentity();
- try {
- if (args != null) {
- for (String arg : args) {
- if ("-h".equals(arg)) {
- pw.println("'dumpsys backup' optional arguments:");
- pw.println(" -h : this help text");
- pw.println(" a[gents] : dump information about defined backup agents");
- return;
- } else if ("agents".startsWith(arg)) {
- dumpAgents(pw);
- return;
- } else if ("transportclients".equals(arg.toLowerCase())) {
- mTransportManager.dumpTransportClients(pw);
- return;
- } else if ("transportstats".equals(arg.toLowerCase())) {
- mTransportManager.dumpTransportStats(pw);
- return;
- }
- }
- }
- dumpInternal(pw);
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- private void dumpAgents(PrintWriter pw) {
- List<PackageInfo> agentPackages = allAgentPackages();
- pw.println("Defined backup agents:");
- for (PackageInfo pkg : agentPackages) {
- pw.print(" ");
- pw.print(pkg.packageName);
- pw.println(':');
- pw.print(" ");
- pw.println(pkg.applicationInfo.backupAgentName);
- }
- }
-
- private void dumpInternal(PrintWriter pw) {
- synchronized (mQueueLock) {
- pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
- + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
- + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
- pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
- if (mBackupRunning) pw.println("Backup currently running");
- pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
- pw.println("Last backup pass started: " + mLastBackupPass
- + " (now = " + System.currentTimeMillis() + ')');
- pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
-
- pw.println("Transport whitelist:");
- for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
- pw.print(" ");
- pw.println(transport.flattenToShortString());
- }
-
- pw.println("Available transports:");
- final String[] transports = listAllTransports();
- if (transports != null) {
- for (String t : transports) {
- pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * "
- : " ") + t);
- try {
- File dir = new File(mBaseStateDir,
- mTransportManager.getTransportDirName(t));
- pw.println(" destination: "
- + mTransportManager.getTransportCurrentDestinationString(t));
- pw.println(" intent: "
- + mTransportManager.getTransportConfigurationIntent(t));
- for (File f : dir.listFiles()) {
- pw.println(
- " " + f.getName() + " - " + f.length() + " state bytes");
- }
- } catch (Exception e) {
- Slog.e(TAG, "Error in transport", e);
- pw.println(" Error: " + e);
- }
- }
- }
-
- mTransportManager.dumpTransportClients(pw);
-
- pw.println("Pending init: " + mPendingInits.size());
- for (String s : mPendingInits) {
- pw.println(" " + s);
- }
-
- pw.print("Ancestral: ");
- pw.println(Long.toHexString(mAncestralToken));
- pw.print("Current: ");
- pw.println(Long.toHexString(mCurrentToken));
-
- int numPackages = mBackupParticipants.size();
- pw.println("Participants:");
- for (int i = 0; i < numPackages; i++) {
- int uid = mBackupParticipants.keyAt(i);
- pw.print(" uid: ");
- pw.println(uid);
- HashSet<String> participants = mBackupParticipants.valueAt(i);
- for (String app : participants) {
- pw.println(" " + app);
- }
- }
-
- pw.println("Ancestral packages: "
- + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
- if (mAncestralPackages != null) {
- for (String pkg : mAncestralPackages) {
- pw.println(" " + pkg);
- }
- }
-
- Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
- pw.println("Ever backed up: " + processedPackages.size());
- for (String pkg : processedPackages) {
- pw.println(" " + pkg);
- }
-
- pw.println("Pending key/value backup: " + mPendingBackups.size());
- for (BackupRequest req : mPendingBackups.values()) {
- pw.println(" " + req);
- }
-
- pw.println("Full backup queue:" + mFullBackupQueue.size());
- for (FullBackupEntry entry : mFullBackupQueue) {
- pw.print(" ");
- pw.print(entry.lastBackup);
- pw.print(" : ");
- pw.println(entry.packageName);
- }
- }
- }
-
-
- public IBackupManager getBackupManagerBinder() {
- return mBackupManagerBinder;
+ mUserBackupManagerService.dump(fd, pw, args);
}
private static boolean backupSettingMigrated(int userId) {
@@ -3620,7 +588,7 @@
return false;
}
- private static void writeBackupEnableState(boolean enable, int userId) {
+ static void writeBackupEnableState(boolean enable, int userId) {
File base = new File(Environment.getDataDirectory(), "backup");
File enableFile = new File(base, BACKUP_ENABLE_FILE);
File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 125c225..92c2ee4 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -5,7 +5,8 @@
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -50,7 +51,7 @@
private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data";
private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new";
- private BackupManagerService mBackupManagerService;
+ private UserBackupManagerService mBackupManagerService;
private final PackageManager mPackageManager;
private final OutputStream mOutput;
private final PackageInfo mCurrentPackage;
@@ -66,7 +67,7 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
- BackupManagerService backupManagerService, PackageManager packageManager,
+ UserBackupManagerService backupManagerService, PackageManager packageManager,
File baseStateDir, File dataDir) {
mOutput = output;
mCurrentPackage = packageInfo;
@@ -85,7 +86,7 @@
mNewStateName = new File(mStateDir,
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
- mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ mManifestFile = new File(mDataDir, BACKUP_MANIFEST_FILENAME);
mAgentTimeoutParameters = Preconditions.checkNotNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bb14576..bed520e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -39,7 +39,7 @@
private static final String TAG = "KeyValueAdbRestoreEngine";
private static final boolean DEBUG = false;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final File mDataDir;
FileMetadata mInfo;
@@ -48,7 +48,7 @@
IBackupAgent mAgent;
int mToken;
- public KeyValueAdbRestoreEngine(BackupManagerService backupManagerService,
+ public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
int token) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index c805783..f2e7435 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -16,6 +16,8 @@
package com.android.server.backup;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -71,7 +73,7 @@
if (delay <= 0) {
delay = interval + new Random().nextInt((int) fuzz);
}
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
}
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
diff --git a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
index b5db5e2..edc2379 100644
--- a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
+++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
@@ -23,8 +23,8 @@
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
-import java.io.FileInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashSet;
@@ -46,7 +46,7 @@
final class ProcessedPackagesJournal {
private static final String TAG = "ProcessedPackagesJournal";
private static final String JOURNAL_FILE_NAME = "processed";
- private static final boolean DEBUG = BackupManagerService.DEBUG || false;
+ private static final boolean DEBUG = BackupManagerService.DEBUG;
// using HashSet instead of ArraySet since we expect 100-500 elements range
@GuardedBy("mProcessedPackages")
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 32fd7e0..59629aa 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -42,18 +42,20 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
/**
- * A proxy to BackupManagerService implementation.
+ * A proxy to the {@link BackupManagerService} implementation.
*
- * <p>This is an external interface to the BackupManagerService which is being accessed via
- * published binder (see BackupManagerService$Lifecycle). This lets us turn down the heavy
+ * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
+ * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
* implementation object on the fly without disturbing binders that have been cached somewhere in
* the system.
*
@@ -143,7 +145,7 @@
}
/**
- * Called from {@link BackupManagerService$Lifecycle} when the system user is unlocked. Attempts
+ * 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()
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
new file mode 100644
index 0000000..fe16afe
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -0,0 +1,3501 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+
+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;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
+import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IBackupAgent;
+import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+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.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerSaveState;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.EventLog;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.EventLogTags;
+import com.android.server.backup.fullbackup.FullBackupEntry;
+import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.ClearDataObserver;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.internal.Operation;
+import com.android.server.backup.internal.PerformInitializeTask;
+import com.android.server.backup.internal.ProvisionedObserver;
+import com.android.server.backup.internal.RunBackupReceiver;
+import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.params.AdbBackupParams;
+import com.android.server.backup.params.AdbParams;
+import com.android.server.backup.params.AdbRestoreParams;
+import com.android.server.backup.params.BackupParams;
+import com.android.server.backup.params.ClearParams;
+import com.android.server.backup.params.ClearRetryParams;
+import com.android.server.backup.params.RestoreParams;
+import com.android.server.backup.restore.ActiveRestoreSession;
+import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.SparseArrayUtils;
+
+import com.google.android.collect.Sets;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** System service that performs backup/restore operations. */
+public class UserBackupManagerService {
+ // File containing backup-enabled state. Contains a single byte;
+ // nonzero == enabled. File missing or contains a zero byte == disabled.
+ private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
+ // Persistently track the need to do a full init.
+ private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+
+ // 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).
+ public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
+ // Name and current contents version of the full-backup manifest file
+ //
+ // Manifest version history:
+ //
+ // 1 : initial release
+ public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+ public static final int BACKUP_MANIFEST_VERSION = 1;
+
+ // External archive format version history:
+ //
+ // 1 : initial release
+ // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+ // 3 : introduced "_meta" metadata file; no other format change per se
+ // 4 : added support for new device-encrypted storage locations
+ // 5 : added support for key-value packages
+ public static final int BACKUP_FILE_VERSION = 5;
+ public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
+ public static final String BACKUP_METADATA_FILENAME = "_meta";
+ public static final int BACKUP_METADATA_VERSION = 1;
+ public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
+
+ private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
+
+ // Round-robin queue for scheduling full backup passes.
+ private static final int SCHEDULE_FILE_VERSION = 1;
+
+ public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
+ public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
+
+ // Pseudoname that we use for the Package Manager metadata "package".
+ public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+
+ // Retry interval for clear/init when the transport is unavailable
+ private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
+
+ public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
+ public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
+ public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+ public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+
+ // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
+ // pending operations list.
+ public static final int OP_PENDING = 0;
+ private static final int OP_ACKNOWLEDGED = 1;
+ private static final int OP_TIMEOUT = -1;
+
+ // Waiting for backup agent to respond during backup operation.
+ public static final int OP_TYPE_BACKUP_WAIT = 0;
+
+ // Waiting for backup agent to respond during restore operation.
+ public static final int OP_TYPE_RESTORE_WAIT = 1;
+
+ // An entire backup operation spanning multiple packages.
+ public static final int OP_TYPE_BACKUP = 2;
+
+ // Time delay for initialization operations that can be delayed so as not to consume too much
+ // CPU on bring-up and increase time-to-UI.
+ private static final long INITIALIZATION_DELAY_MILLIS = 3000;
+
+ // Timeout interval for deciding that a bind or clear-data has taken too long
+ private static final long TIMEOUT_INTERVAL = 10 * 1000;
+
+ // User confirmation timeout for a full backup/restore operation. It's this long in
+ // order to give them time to enter the backup password.
+ private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
+
+ // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+ // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+ private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
+ private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ private final TransportManager mTransportManager;
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private IPackageManager mPackageManagerBinder;
+ private IActivityManager mActivityManager;
+ private PowerManager mPowerManager;
+ private AlarmManager mAlarmManager;
+ private IStorageManager mStorageManager;
+ private BackupManagerConstants mConstants;
+ private PowerManager.WakeLock mWakelock;
+ private BackupHandler mBackupHandler;
+
+ private IBackupManager mBackupManagerBinder;
+
+ private boolean mEnabled; // access to this is synchronized on 'this'
+ private boolean mProvisioned;
+ private boolean mAutoRestore;
+
+ private PendingIntent mRunBackupIntent;
+ private PendingIntent mRunInitIntent;
+
+ private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
+
+ // map UIDs to the set of participating packages under that UID
+ private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
+
+ // Backups that we haven't started yet. Keys are package names.
+ private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+
+ // locking around the pending-backup management
+ private final Object mQueueLock = new Object();
+
+ // The thread performing the sequence of queued backups binds to each app's agent
+ // in succession. Bind notifications are asynchronously delivered through the
+ // Activity Manager; use this lock object to signal when a requested binding has
+ // completed.
+ private final Object mAgentConnectLock = new Object();
+ private IBackupAgent mConnectedAgent;
+ private volatile boolean mConnecting;
+
+ private volatile boolean mBackupRunning;
+ private volatile long mLastBackupPass;
+
+ // A similar synchronization mechanism around clearing apps' data for restore
+ private final Object mClearDataLock = new Object();
+ private volatile boolean mClearingData;
+
+ // Used by ADB.
+ private final BackupPasswordManager mBackupPasswordManager;
+ private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
+ private final SecureRandom mRng = new SecureRandom();
+
+ // Time when we post the transport registration operation
+ private final long mRegisterTransportsRequestedTime;
+
+ @GuardedBy("mQueueLock")
+ private PerformFullTransportBackupTask mRunningFullBackupTask;
+
+ @GuardedBy("mQueueLock")
+ private ArrayList<FullBackupEntry> mFullBackupQueue;
+
+ @GuardedBy("mPendingRestores")
+ private boolean mIsRestoreInProgress;
+
+ @GuardedBy("mPendingRestores")
+ private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
+
+ private ActiveRestoreSession mActiveRestoreSession;
+
+ // Watch the device provisioning operation during setup
+ private ContentObserver mProvisionedObserver;
+
+ /**
+ * mCurrentOperations contains the list of currently active operations.
+ *
+ * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+ * An operation wraps a BackupRestoreTask within it.
+ * It's the responsibility of this task to remove the operation from this array.
+ *
+ * A BackupRestore task gets notified of ack/timeout for the operation via
+ * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+ * on the mCurrentOpLock.
+ * {@link UserBackupManagerService#waitUntilOperationComplete(int)} is
+ * used in various places to 'wait' for notifyAll and detect change of pending state of an
+ * operation. So typically, an operation will be removed from this array by:
+ * - BackupRestoreTask#handleCancel and
+ * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+ * these places because waitUntilOperationComplete relies on the operation being present to
+ * determine its completion status.
+ *
+ * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+ * cancel backup tasks.
+ */
+ @GuardedBy("mCurrentOpLock")
+ private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
+ private final Object mCurrentOpLock = new Object();
+ private final Random mTokenGenerator = new Random();
+ final AtomicInteger mNextToken = new AtomicInteger();
+
+ // Where we keep our journal files and other bookkeeping.
+ private File mBaseStateDir;
+ private File mDataDir;
+ private File mJournalDir;
+ @Nullable
+ private DataChangedJournal mJournal;
+ private File mFullBackupScheduleFile;
+
+ // Keep a log of all the apps we've ever backed up.
+ private ProcessedPackagesJournal mProcessedPackagesJournal;
+
+ private File mTokenFile;
+ private Set<String> mAncestralPackages = null;
+ private long mAncestralToken = 0;
+ private long mCurrentToken = 0;
+
+ @VisibleForTesting
+ public UserBackupManagerService(
+ Context context,
+ Trampoline parent,
+ HandlerThread backupThread,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mPackageManagerBinder = AppGlobals.getPackageManager();
+ mActivityManager = ActivityManager.getService();
+
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+
+ mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+
+ mAgentTimeoutParameters = new
+ BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+ mAgentTimeoutParameters.start();
+
+ // spin up the backup/restore handler thread
+ mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+
+ // Set up our bookkeeping
+ final ContentResolver resolver = context.getContentResolver();
+ mProvisioned = Settings.Global.getInt(resolver,
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ mAutoRestore = Settings.Secure.getInt(resolver,
+ Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+ mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mProvisionedObserver);
+
+ mBaseStateDir = baseStateDir;
+ mBaseStateDir.mkdirs();
+ if (!SELinux.restorecon(mBaseStateDir)) {
+ Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+ }
+
+ mDataDir = dataDir;
+
+ mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
+
+ // Alarm receivers for scheduled backups & initialization operations
+ BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(RUN_BACKUP_ACTION);
+ context.registerReceiver(mRunBackupReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+ filter = new IntentFilter();
+ filter.addAction(RUN_INITIALIZE_ACTION);
+ context.registerReceiver(mRunInitReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+ backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+
+ Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+ initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+
+ // Set up the backup-request journaling
+ mJournalDir = new File(mBaseStateDir, "pending");
+ mJournalDir.mkdirs(); // creates mBaseStateDir along the way
+ mJournal = null; // will be created on first use
+
+ mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+ // We are observing changes to the constants throughout the lifecycle of BMS. This is
+ // because we reference the constants in multiple areas of BMS, which otherwise would
+ // require frequent starting and stopping.
+ mConstants.start();
+
+ // Set up the various sorts of package tracking we do
+ mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+ initPackageTracking();
+
+ // Build our mapping of uid to backup client services. This implicitly
+ // schedules a backup pass on the Package Manager metadata the first
+ // time anything needs to be backed up.
+ synchronized (mBackupParticipants) {
+ addPackageParticipantsLocked(null);
+ }
+
+ mTransportManager = transportManager;
+ mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+ mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+ mBackupHandler.postDelayed(
+ mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
+
+ // Now that we know about valid backup participants, parse any leftover journal files into
+ // the pending backup set
+ mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+
+ // Power management
+ mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+ }
+
+
+ public BackupManagerConstants getConstants() {
+ return mConstants;
+ }
+
+ public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+ return mAgentTimeoutParameters;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ public void setPackageManager(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ public IPackageManager getPackageManagerBinder() {
+ return mPackageManagerBinder;
+ }
+
+ public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
+ mPackageManagerBinder = packageManagerBinder;
+ }
+
+ public IActivityManager getActivityManager() {
+ return mActivityManager;
+ }
+
+ public void setActivityManager(IActivityManager activityManager) {
+ mActivityManager = activityManager;
+ }
+
+ public AlarmManager getAlarmManager() {
+ return mAlarmManager;
+ }
+
+ public void setAlarmManager(AlarmManager alarmManager) {
+ mAlarmManager = alarmManager;
+ }
+
+ @VisibleForTesting
+ void setPowerManager(PowerManager powerManager) {
+ mPowerManager = powerManager;
+ }
+
+ public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
+ mBackupManagerBinder = backupManagerBinder;
+ }
+
+ public TransportManager getTransportManager() {
+ return mTransportManager;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ public boolean isProvisioned() {
+ return mProvisioned;
+ }
+
+ public void setProvisioned(boolean provisioned) {
+ mProvisioned = provisioned;
+ }
+
+ public PowerManager.WakeLock getWakelock() {
+ return mWakelock;
+ }
+
+ /**
+ * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
+ * #getWakelock()}.
+ */
+ @VisibleForTesting
+ public void setWorkSource(@Nullable WorkSource workSource) {
+ // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
+ mWakelock.setWorkSource(workSource);
+ }
+
+ public void setWakelock(PowerManager.WakeLock wakelock) {
+ mWakelock = wakelock;
+ }
+
+ public Handler getBackupHandler() {
+ return mBackupHandler;
+ }
+
+ public void setBackupHandler(BackupHandler backupHandler) {
+ mBackupHandler = backupHandler;
+ }
+
+ public PendingIntent getRunInitIntent() {
+ return mRunInitIntent;
+ }
+
+ public void setRunInitIntent(PendingIntent runInitIntent) {
+ mRunInitIntent = runInitIntent;
+ }
+
+ public HashMap<String, BackupRequest> getPendingBackups() {
+ return mPendingBackups;
+ }
+
+ public void setPendingBackups(
+ HashMap<String, BackupRequest> pendingBackups) {
+ mPendingBackups = pendingBackups;
+ }
+
+ public Object getQueueLock() {
+ return mQueueLock;
+ }
+
+ public boolean isBackupRunning() {
+ return mBackupRunning;
+ }
+
+ public void setBackupRunning(boolean backupRunning) {
+ mBackupRunning = backupRunning;
+ }
+
+ public long getLastBackupPass() {
+ return mLastBackupPass;
+ }
+
+ public void setLastBackupPass(long lastBackupPass) {
+ mLastBackupPass = lastBackupPass;
+ }
+
+ public Object getClearDataLock() {
+ return mClearDataLock;
+ }
+
+ public boolean isClearingData() {
+ return mClearingData;
+ }
+
+ public void setClearingData(boolean clearingData) {
+ mClearingData = clearingData;
+ }
+
+ public boolean isRestoreInProgress() {
+ return mIsRestoreInProgress;
+ }
+
+ public void setRestoreInProgress(boolean restoreInProgress) {
+ mIsRestoreInProgress = restoreInProgress;
+ }
+
+ public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
+ return mPendingRestores;
+ }
+
+ public ActiveRestoreSession getActiveRestoreSession() {
+ return mActiveRestoreSession;
+ }
+
+ public void setActiveRestoreSession(
+ ActiveRestoreSession activeRestoreSession) {
+ mActiveRestoreSession = activeRestoreSession;
+ }
+
+ public SparseArray<Operation> getCurrentOperations() {
+ return mCurrentOperations;
+ }
+
+ public Object getCurrentOpLock() {
+ return mCurrentOpLock;
+ }
+
+ public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
+ return mAdbBackupRestoreConfirmations;
+ }
+
+ public File getBaseStateDir() {
+ return mBaseStateDir;
+ }
+
+ public void setBaseStateDir(File baseStateDir) {
+ mBaseStateDir = baseStateDir;
+ }
+
+ public File getDataDir() {
+ return mDataDir;
+ }
+
+ public void setDataDir(File dataDir) {
+ mDataDir = dataDir;
+ }
+
+ @Nullable
+ public DataChangedJournal getJournal() {
+ return mJournal;
+ }
+
+ public void setJournal(@Nullable DataChangedJournal journal) {
+ mJournal = journal;
+ }
+
+ public SecureRandom getRng() {
+ return mRng;
+ }
+
+ public Set<String> getAncestralPackages() {
+ return mAncestralPackages;
+ }
+
+ public void setAncestralPackages(Set<String> ancestralPackages) {
+ mAncestralPackages = ancestralPackages;
+ }
+
+ public long getAncestralToken() {
+ return mAncestralToken;
+ }
+
+ public void setAncestralToken(long ancestralToken) {
+ mAncestralToken = ancestralToken;
+ }
+
+ public long getCurrentToken() {
+ return mCurrentToken;
+ }
+
+ public void setCurrentToken(long currentToken) {
+ mCurrentToken = currentToken;
+ }
+
+ public ArraySet<String> getPendingInits() {
+ return mPendingInits;
+ }
+
+ /** Clear all pending transport initializations. */
+ public void clearPendingInits() {
+ mPendingInits.clear();
+ }
+
+ public PerformFullTransportBackupTask getRunningFullBackupTask() {
+ return mRunningFullBackupTask;
+ }
+
+ public void setRunningFullBackupTask(
+ PerformFullTransportBackupTask runningFullBackupTask) {
+ mRunningFullBackupTask = runningFullBackupTask;
+ }
+
+ /**
+ * Utility: build a new random integer token. The low bits are the ordinal of the operation for
+ * near-time uniqueness, and the upper bits are random for app-side unpredictability.
+ */
+ public int generateRandomIntegerToken() {
+ int token = mTokenGenerator.nextInt();
+ if (token < 0) token = -token;
+ token &= ~0xFF;
+ token |= (mNextToken.incrementAndGet() & 0xFF);
+ return token;
+ }
+
+ /**
+ * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
+ * non-lifecycle agent instance, so we manually set up the context topology for it.
+ */
+ public BackupAgent makeMetadataAgent() {
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
+ /**
+ * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+ */
+ public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+ PackageManagerBackupAgent pmAgent =
+ new PackageManagerBackupAgent(mPackageManager, packages);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
+ private void initPackageTracking() {
+ if (MORE_DEBUG) Slog.v(TAG, "` tracking");
+
+ // Remember our ancestral dataset
+ mTokenFile = new File(mBaseStateDir, "ancestral");
+ try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
+ new FileInputStream(mTokenFile)))) {
+ int version = tokenStream.readInt();
+ if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
+ mAncestralToken = tokenStream.readLong();
+ mCurrentToken = tokenStream.readLong();
+
+ int numPackages = tokenStream.readInt();
+ if (numPackages >= 0) {
+ mAncestralPackages = new HashSet<>();
+ for (int i = 0; i < numPackages; i++) {
+ String pkgName = tokenStream.readUTF();
+ mAncestralPackages.add(pkgName);
+ }
+ }
+ }
+ } catch (FileNotFoundException fnf) {
+ // Probably innocuous
+ Slog.v(TAG, "No ancestral data");
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to read token file", e);
+ }
+
+ mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
+ mProcessedPackagesJournal.init();
+
+ synchronized (mQueueLock) {
+ // Resume the full-data backup queue
+ mFullBackupQueue = readFullBackupSchedule();
+ }
+
+ // Register for broadcasts about package install, etc., so we can
+ // update the provider list.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ // Register for events related to sdcard installation.
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ }
+
+ private ArrayList<FullBackupEntry> readFullBackupSchedule() {
+ boolean changed = false;
+ ArrayList<FullBackupEntry> schedule = null;
+ List<PackageInfo> apps =
+ PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+
+ if (mFullBackupScheduleFile.exists()) {
+ try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
+ BufferedInputStream bufStream = new BufferedInputStream(fstream);
+ DataInputStream in = new DataInputStream(bufStream)) {
+ int version = in.readInt();
+ if (version != SCHEDULE_FILE_VERSION) {
+ Slog.e(TAG, "Unknown backup schedule version " + version);
+ return null;
+ }
+
+ final int numPackages = in.readInt();
+ schedule = new ArrayList<>(numPackages);
+
+ // HashSet instead of ArraySet specifically because we want the eventual
+ // lookups against O(hundreds) of entries to be as fast as possible, and
+ // we discard the set immediately after the scan so the extra memory
+ // overhead is transient.
+ HashSet<String> foundApps = new HashSet<>(numPackages);
+
+ for (int i = 0; i < numPackages; i++) {
+ String pkgName = in.readUTF();
+ long lastBackup = in.readLong();
+ foundApps.add(pkgName); // all apps that we've addressed already
+ try {
+ PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
+ if (AppBackupUtils.appGetsFullBackup(pkg)
+ && AppBackupUtils.appIsEligibleForBackup(
+ pkg.applicationInfo, mPackageManager)) {
+ schedule.add(new FullBackupEntry(pkgName, lastBackup));
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Package " + pkgName
+ + " no longer eligible for full backup");
+ }
+ }
+ } catch (NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.i(TAG, "Package " + pkgName
+ + " not installed; dropping from full backup");
+ }
+ }
+ }
+
+ // New apps can arrive "out of band" via OTA and similar, so we also need to
+ // scan to make sure that we're tracking all full-backup candidates properly
+ for (PackageInfo app : apps) {
+ if (AppBackupUtils.appGetsFullBackup(app)
+ && AppBackupUtils.appIsEligibleForBackup(
+ app.applicationInfo, mPackageManager)) {
+ if (!foundApps.contains(app.packageName)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "New full backup app " + app.packageName + " found");
+ }
+ schedule.add(new FullBackupEntry(app.packageName, 0));
+ changed = true;
+ }
+ }
+ }
+
+ Collections.sort(schedule);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to read backup schedule", e);
+ mFullBackupScheduleFile.delete();
+ schedule = null;
+ }
+ }
+
+ if (schedule == null) {
+ // no prior queue record, or unable to read it. Set up the queue
+ // from scratch.
+ changed = true;
+ schedule = new ArrayList<>(apps.size());
+ for (PackageInfo info : apps) {
+ if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
+ info.applicationInfo, mPackageManager)) {
+ schedule.add(new FullBackupEntry(info.packageName, 0));
+ }
+ }
+ }
+
+ if (changed) {
+ writeFullBackupScheduleAsync();
+ }
+ return schedule;
+ }
+
+ private Runnable mFullBackupScheduleWriter = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mQueueLock) {
+ try {
+ ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
+ DataOutputStream bufOut = new DataOutputStream(bufStream);
+ bufOut.writeInt(SCHEDULE_FILE_VERSION);
+
+ // version 1:
+ //
+ // [int] # of packages in the queue = N
+ // N * {
+ // [utf8] package name
+ // [long] last backup time for this package
+ // }
+ int numPackages = mFullBackupQueue.size();
+ bufOut.writeInt(numPackages);
+
+ for (int i = 0; i < numPackages; i++) {
+ FullBackupEntry entry = mFullBackupQueue.get(i);
+ bufOut.writeUTF(entry.packageName);
+ bufOut.writeLong(entry.lastBackup);
+ }
+ bufOut.flush();
+
+ AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
+ FileOutputStream out = af.startWrite();
+ out.write(bufStream.toByteArray());
+ af.finishWrite(out);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to write backup schedule!", e);
+ }
+ }
+ }
+ };
+
+ private void writeFullBackupScheduleAsync() {
+ mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
+ mBackupHandler.post(mFullBackupScheduleWriter);
+ }
+
+ private void parseLeftoverJournals() {
+ ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
+ for (DataChangedJournal journal : journals) {
+ if (!journal.equals(mJournal)) {
+ try {
+ journal.forEach(packageName -> {
+ Slog.i(TAG, "Found stale backup journal, scheduling");
+ if (MORE_DEBUG) Slog.i(TAG, " " + packageName);
+ dataChangedImpl(packageName);
+ });
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't read " + journal, e);
+ }
+ }
+ }
+ }
+
+ /** Used for generating random salts or passwords. */
+ public byte[] randomBytes(int bits) {
+ byte[] array = new byte[bits / 8];
+ mRng.nextBytes(array);
+ return array;
+ }
+
+ /** For adb backup/restore. */
+ public boolean setBackupPassword(String currentPw, String newPw) {
+ return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
+ }
+
+ /** For adb backup/restore. */
+ public boolean hasBackupPassword() {
+ return mBackupPasswordManager.hasBackupPassword();
+ }
+
+ /** For adb backup/restore. */
+ public boolean backupPasswordMatches(String currentPw) {
+ return mBackupPasswordManager.backupPasswordMatches(currentPw);
+ }
+
+ /**
+ * Maintain persistent state around whether need to do an initialize operation. This will lock
+ * on {@link #getQueueLock()}.
+ */
+ public void recordInitPending(
+ boolean isPending, String transportName, String transportDirName) {
+ synchronized (mQueueLock) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
+ }
+
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+
+ if (isPending) {
+ // We need an init before we can proceed with sending backup data.
+ // Record that with an entry in our set of pending inits, as well as
+ // journaling it via creation of a sentinel file.
+ mPendingInits.add(transportName);
+ try {
+ (new FileOutputStream(initPendingFile)).close();
+ } catch (IOException ioe) {
+ // Something is badly wrong with our permissions; just try to move on
+ }
+ } else {
+ // No more initialization needed; wipe the journal and reset our state.
+ initPendingFile.delete();
+ mPendingInits.remove(transportName);
+ }
+ }
+ }
+
+ /**
+ * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
+ * expiry), so we must re-upload all saved settings.
+ */
+ public void resetBackupState(File stateFileDir) {
+ synchronized (mQueueLock) {
+ mProcessedPackagesJournal.reset();
+
+ mCurrentToken = 0;
+ writeRestoreTokens();
+
+ // Remove all the state files
+ for (File sf : stateFileDir.listFiles()) {
+ // ... but don't touch the needs-init sentinel
+ if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
+ sf.delete();
+ }
+ }
+ }
+
+ // Enqueue a new backup of every participant
+ synchronized (mBackupParticipants) {
+ final int numParticipants = mBackupParticipants.size();
+ for (int i = 0; i < numParticipants; i++) {
+ HashSet<String> participants = mBackupParticipants.valueAt(i);
+ if (participants != null) {
+ for (String packageName : participants) {
+ dataChangedImpl(packageName);
+ }
+ }
+ }
+ }
+ }
+
+ private void onTransportRegistered(String transportName, String transportDirName) {
+ if (DEBUG) {
+ long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+ Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
+ + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+ }
+
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ stateDir.mkdirs();
+
+ File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ if (initSentinel.exists()) {
+ synchronized (mQueueLock) {
+ mPendingInits.add(transportName);
+
+ // TODO: pick a better starting time than now + 1 minute
+ long delay = 1000 * 60; // one minute, in milliseconds
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay, mRunInitIntent);
+ }
+ }
+ }
+
+ // ----- Track installation/removal of packages -----
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+
+ String action = intent.getAction();
+ boolean replacing = false;
+ boolean added = false;
+ boolean changed = false;
+ Bundle extras = intent.getExtras();
+ String[] pkgList = null;
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ final String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName != null) {
+ pkgList = new String[]{pkgName};
+ }
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+
+ // At package-changed we only care about looking at new transport states
+ if (changed) {
+ final String[] components =
+ intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+ for (int i = 0; i < components.length; i++) {
+ Slog.i(TAG, " * " + components[i]);
+ }
+ }
+
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageChanged(pkgName, components));
+ return; // nothing more to do in the PACKAGE_CHANGED case
+ }
+
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ added = true;
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ added = false;
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ }
+
+ if (pkgList == null || pkgList.length == 0) {
+ return;
+ }
+
+ final int uid = extras.getInt(Intent.EXTRA_UID);
+ if (added) {
+ synchronized (mBackupParticipants) {
+ if (replacing) {
+ // This is the package-replaced case; we just remove the entry
+ // under the old uid and fall through to re-add. If an app
+ // just added key/value backup participation, this picks it up
+ // as a known participant.
+ removePackageParticipantsLocked(pkgList, uid);
+ }
+ addPackageParticipantsLocked(pkgList);
+ }
+ // If they're full-backup candidates, add them there instead
+ final long now = System.currentTimeMillis();
+ for (final String packageName : pkgList) {
+ try {
+ PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+ if (AppBackupUtils.appGetsFullBackup(app)
+ && AppBackupUtils.appIsEligibleForBackup(
+ app.applicationInfo, mPackageManager)) {
+ enqueueFullBackup(packageName, now);
+ scheduleNextFullBackupJob(0);
+ } else {
+ // The app might have just transitioned out of full-data into
+ // doing key/value backups, or might have just disabled backups
+ // entirely. Make sure it is no longer in the full-data queue.
+ synchronized (mQueueLock) {
+ dequeueFullBackupLocked(packageName);
+ }
+ writeFullBackupScheduleAsync();
+ }
+
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
+
+ } catch (NameNotFoundException e) {
+ // doesn't really exist; ignore it
+ if (DEBUG) {
+ Slog.w(TAG, "Can't resolve new app " + packageName);
+ }
+ }
+ }
+
+ // Whenever a package is added or updated we need to update
+ // the package metadata bookkeeping.
+ dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
+ } else {
+ if (replacing) {
+ // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+ } else {
+ // Outright removal. In the full-data case, the app will be dropped
+ // from the queue when its (now obsolete) name comes up again for
+ // backup.
+ synchronized (mBackupParticipants) {
+ removePackageParticipantsLocked(pkgList, uid);
+ }
+ }
+ for (final String pkgName : pkgList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(pkgName));
+ }
+ }
+ }
+ };
+
+ // Add the backup agents in the given packages to our set of known backup participants.
+ // If 'packageNames' is null, adds all backup agents in the whole system.
+ private void addPackageParticipantsLocked(String[] packageNames) {
+ // Look for apps that define the android:backupAgent attribute
+ List<PackageInfo> targetApps = allAgentPackages();
+ if (packageNames != null) {
+ if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
+ for (String packageName : packageNames) {
+ addPackageParticipantsLockedInner(packageName, targetApps);
+ }
+ } else {
+ if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
+ addPackageParticipantsLockedInner(null, targetApps);
+ }
+ }
+
+ private void addPackageParticipantsLockedInner(String packageName,
+ List<PackageInfo> targetPkgs) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Examining " + packageName + " for backup agent");
+ }
+
+ for (PackageInfo pkg : targetPkgs) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
+ HashSet<String> set = mBackupParticipants.get(uid);
+ if (set == null) {
+ set = new HashSet<>();
+ mBackupParticipants.put(uid, set);
+ }
+ set.add(pkg.packageName);
+ if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
+
+ // Schedule a backup for it on general principles
+ if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+ Message msg = mBackupHandler
+ .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
+ mBackupHandler.sendMessage(msg);
+ }
+ }
+ }
+
+ // Remove the given packages' entries from our known active set.
+ private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
+ if (packageNames == null) {
+ Slog.w(TAG, "removePackageParticipants with null list");
+ return;
+ }
+
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
+ + " #" + packageNames.length);
+ }
+ for (String pkg : packageNames) {
+ // Known previous UID, so we know which package set to check
+ HashSet<String> set = mBackupParticipants.get(oldUid);
+ if (set != null && set.contains(pkg)) {
+ removePackageFromSetLocked(set, pkg);
+ if (set.isEmpty()) {
+ if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
+ mBackupParticipants.remove(oldUid);
+ }
+ }
+ }
+ }
+
+ private void removePackageFromSetLocked(final HashSet<String> set,
+ final String packageName) {
+ if (set.contains(packageName)) {
+ // Found it. Remove this one package from the bookkeeping, and
+ // if it's the last participating app under this uid we drop the
+ // (now-empty) set as well.
+ // Note that we deliberately leave it 'known' in the "ever backed up"
+ // bookkeeping so that its current-dataset data will be retrieved
+ // if the app is subsequently reinstalled
+ if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
+ set.remove(packageName);
+ mPendingBackups.remove(packageName);
+ }
+ }
+
+ // Returns the set of all applications that define an android:backupAgent attribute
+ private List<PackageInfo> allAgentPackages() {
+ // !!! TODO: cache this and regenerate only when necessary
+ int flags = PackageManager.GET_SIGNING_CERTIFICATES;
+ List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+ int numPackages = packages.size();
+ for (int a = numPackages - 1; a >= 0; a--) {
+ PackageInfo pkg = packages.get(a);
+ try {
+ ApplicationInfo app = pkg.applicationInfo;
+ if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+ || app.backupAgentName == null
+ || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
+ packages.remove(a);
+ } else {
+ // we will need the shared library path, so look that up and store it here.
+ // This is used implicitly when we pass the PackageInfo object off to
+ // the Activity Manager to launch the app for backup/restore purposes.
+ app = mPackageManager.getApplicationInfo(pkg.packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES);
+ pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
+ pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
+ }
+ } catch (NameNotFoundException e) {
+ packages.remove(a);
+ }
+ }
+ return packages;
+ }
+
+ /**
+ * Called from the backup tasks: record that the given app has been successfully backed up at
+ * least once. This includes both key/value and full-data backups through the transport.
+ */
+ public void logBackupComplete(String packageName) {
+ if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
+
+ for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
+ final Intent notification = new Intent();
+ notification.setAction(BACKUP_FINISHED_ACTION);
+ notification.setPackage(receiver);
+ notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
+ mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
+ }
+
+ mProcessedPackagesJournal.addPackage(packageName);
+ }
+
+ /**
+ * Persistently record the current and ancestral backup tokens, as well as the set of packages
+ * with data available in the ancestral dataset.
+ */
+ public void writeRestoreTokens() {
+ try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
+ // First, the version number of this record, for futureproofing
+ af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
+
+ // Write the ancestral and current tokens
+ af.writeLong(mAncestralToken);
+ af.writeLong(mCurrentToken);
+
+ // Now write the set of ancestral packages
+ if (mAncestralPackages == null) {
+ af.writeInt(-1);
+ } else {
+ af.writeInt(mAncestralPackages.size());
+ if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
+ for (String pkgName : mAncestralPackages) {
+ af.writeUTF(pkgName);
+ if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
+ }
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to write token file:", e);
+ }
+ }
+
+ /** Fires off a backup agent, blocking until it attaches or times out. */
+ @Nullable
+ public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
+ IBackupAgent agent = null;
+ synchronized (mAgentConnectLock) {
+ mConnecting = true;
+ mConnectedAgent = null;
+ try {
+ if (mActivityManager.bindBackupAgent(app.packageName, mode,
+ UserHandle.USER_OWNER)) {
+ Slog.d(TAG, "awaiting agent for " + app);
+
+ // success; wait for the agent to arrive
+ // only wait 10 seconds for the bind to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mConnecting && mConnectedAgent == null
+ && (System.currentTimeMillis() < timeoutMark)) {
+ try {
+ mAgentConnectLock.wait(5000);
+ } catch (InterruptedException e) {
+ // just bail
+ Slog.w(TAG, "Interrupted: " + e);
+ mConnecting = false;
+ mConnectedAgent = null;
+ }
+ }
+
+ // if we timed out with no connect, abort and move on
+ if (mConnecting) {
+ Slog.w(TAG, "Timeout waiting for agent " + app);
+ mConnectedAgent = null;
+ }
+ if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
+ agent = mConnectedAgent;
+ }
+ } catch (RemoteException e) {
+ // can't happen - ActivityManager is local
+ }
+ }
+ if (agent == null) {
+ try {
+ mActivityManager.clearPendingBackup();
+ } catch (RemoteException e) {
+ // can't happen - ActivityManager is local
+ }
+ }
+ return agent;
+ }
+
+ /** Unbind from a backup agent. */
+ public void unbindAgent(ApplicationInfo app) {
+ try {
+ mActivityManager.unbindBackupAgent(app);
+ } catch (RemoteException e) {
+ // Can't happen - activity manager is local
+ }
+ }
+
+ /**
+ * Clear an application's data, blocking until the operation completes or times out. If {@code
+ * keepSystemState} is {@code true}, we intentionally do not clear system state that would
+ * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
+ * bringing it into the actual expected state related to the already-restored notification state
+ * etc.
+ */
+ public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
+ // Don't wipe packages marked allowClearUserData=false
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "allowClearUserData=false so not wiping "
+ + packageName);
+ }
+ return;
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
+ return;
+ }
+
+ ClearDataObserver observer = new ClearDataObserver(this);
+
+ synchronized (mClearDataLock) {
+ mClearingData = true;
+ try {
+ mActivityManager.clearApplicationUserData(
+ packageName, keepSystemState, observer, 0);
+ } catch (RemoteException e) {
+ // can't happen because the activity manager is in this process
+ }
+
+ // only wait 10 seconds for the clear data to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+ try {
+ mClearDataLock.wait(5000);
+ } catch (InterruptedException e) {
+ // won't happen, but still.
+ mClearingData = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the restore-set token for the best-available restore set for this {@code packageName}:
+ * the active set if possible, else the ancestral one. Returns zero if none available.
+ */
+ public long getAvailableRestoreToken(String packageName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getAvailableRestoreToken");
+
+ long token = mAncestralToken;
+ synchronized (mQueueLock) {
+ if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "App in ever-stored, so using current token");
+ }
+ token = mCurrentToken;
+ }
+ }
+ if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
+ return token;
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages}.
+ *
+ * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
+ */
+ public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
+ return requestBackup(packages, observer, null, flags);
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages} with a specified {@link
+ * IBackupManagerMonitor}.
+ */
+ public int requestBackup(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
+
+ if (packages == null || packages.length < 1) {
+ Slog.e(TAG, "No packages named for backup request");
+ BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+ monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+ BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
+ null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ throw new IllegalArgumentException("No packages are provided for backup");
+ }
+
+ if (!mEnabled || !mProvisioned) {
+ Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+ BackupObserverUtils.sendBackupFinished(observer,
+ BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ final int logTag = mProvisioned
+ ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
+ : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
+ monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+ }
+
+ final TransportClient transportClient;
+ final String transportDirName;
+ try {
+ transportDirName =
+ mTransportManager.getTransportDirName(
+ mTransportManager.getCurrentTransportName());
+ transportClient =
+ mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
+ } catch (TransportNotRegisteredException e) {
+ BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+ monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
+ null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ return BackupManager.ERROR_TRANSPORT_ABORTED;
+ }
+
+ OnTaskFinishedListener listener =
+ caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
+
+ ArrayList<String> fullBackupList = new ArrayList<>();
+ ArrayList<String> kvBackupList = new ArrayList<>();
+ for (String packageName : packages) {
+ if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+ kvBackupList.add(packageName);
+ continue;
+ }
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
+ mPackageManager)) {
+ BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+ BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ continue;
+ }
+ if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+ fullBackupList.add(packageInfo.packageName);
+ } else {
+ kvBackupList.add(packageInfo.packageName);
+ }
+ } catch (NameNotFoundException e) {
+ BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+ BackupManager.ERROR_PACKAGE_NOT_FOUND);
+ }
+ }
+ EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
+ fullBackupList.size());
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
+ + fullBackupList.size() + " full backups, " + kvBackupList.size()
+ + " k/v backups");
+ }
+
+ boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
+
+ Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
+ msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
+ observer, monitor, listener, true, nonIncrementalBackup);
+ mBackupHandler.sendMessage(msg);
+ return BackupManager.SUCCESS;
+ }
+
+ /** Cancel all running backups. */
+ public void cancelBackups() {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "cancelBackups() called.");
+ }
+ final long oldToken = Binder.clearCallingIdentity();
+ try {
+ List<Integer> operationsToCancel = new ArrayList<>();
+ synchronized (mCurrentOpLock) {
+ for (int i = 0; i < mCurrentOperations.size(); i++) {
+ Operation op = mCurrentOperations.valueAt(i);
+ int token = mCurrentOperations.keyAt(i);
+ if (op.type == OP_TYPE_BACKUP) {
+ operationsToCancel.add(token);
+ }
+ }
+ }
+ for (Integer token : operationsToCancel) {
+ handleCancel(token, true /* cancelAll */);
+ }
+ // We don't want the backup jobs to kick in any time soon.
+ // Reschedules them to run in the distant future.
+ KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
+ }
+ }
+
+ /** Schedule a timeout message for the operation identified by {@code token}. */
+ public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
+ int operationType) {
+ if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
+ Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
+ + Integer.toHexString(token) + " of type " + operationType);
+ return;
+ }
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ + " interval=" + interval + " callback=" + callback);
+ }
+
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
+ Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
+ token, 0, callback);
+ mBackupHandler.sendMessageDelayed(msg, interval);
+ }
+ }
+
+ private int getMessageIdForOperationType(int operationType) {
+ switch (operationType) {
+ case OP_TYPE_BACKUP_WAIT:
+ return MSG_BACKUP_OPERATION_TIMEOUT;
+ case OP_TYPE_RESTORE_WAIT:
+ return MSG_RESTORE_OPERATION_TIMEOUT;
+ default:
+ Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
+ + operationType);
+ return -1;
+ }
+ }
+
+ /**
+ * Add an operation to the list of currently running operations. Used for cancellation,
+ * completion and timeout callbacks that act on the operation via the {@code token}.
+ */
+ public void putOperation(int token, Operation operation) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
+ + operation.type);
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, operation);
+ }
+ }
+
+ /**
+ * Remove an operation from the list of currently running operations. An operation is removed
+ * when it is completed, cancelled, or timed out, and thus no longer running.
+ */
+ public void removeOperation(int token) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
+ }
+ synchronized (mCurrentOpLock) {
+ if (mCurrentOperations.get(token) == null) {
+ Slog.w(TAG, "Duplicate remove for operation. token="
+ + Integer.toHexString(token));
+ }
+ mCurrentOperations.remove(token);
+ }
+ }
+
+ /** Block until we received an operation complete message (from the agent or cancellation). */
+ public boolean waitUntilOperationComplete(int token) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Blocking until operation complete for "
+ + Integer.toHexString(token));
+ }
+ int finalState = OP_PENDING;
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ while (true) {
+ op = mCurrentOperations.get(token);
+ if (op == null) {
+ // mysterious disappearance: treat as success with no callback
+ break;
+ } else {
+ if (op.state == OP_PENDING) {
+ try {
+ mCurrentOpLock.wait();
+ } catch (InterruptedException e) {
+ }
+ // When the wait is notified we loop around and recheck the current state
+ } else {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Unblocked waiting for operation token="
+ + Integer.toHexString(token));
+ }
+ // No longer pending; we're done
+ finalState = op.state;
+ break;
+ }
+ }
+ }
+ }
+
+ removeOperation(token);
+ if (op != null) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+ }
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "operation " + Integer.toHexString(token)
+ + " complete: finalState=" + finalState);
+ }
+ return finalState == OP_ACKNOWLEDGED;
+ }
+
+ /** Cancel the operation associated with {@code token}. */
+ public void handleCancel(int token, boolean cancelAll) {
+ // Notify any synchronous waiters
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ op = mCurrentOperations.get(token);
+ if (MORE_DEBUG) {
+ if (op == null) {
+ Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
+ + " but no op found");
+ }
+ }
+ int state = (op != null) ? op.state : OP_TIMEOUT;
+ if (state == OP_ACKNOWLEDGED) {
+ // The operation finished cleanly, so we have nothing more to do.
+ if (DEBUG) {
+ Slog.w(TAG, "Operation already got an ack."
+ + "Should have been removed from mCurrentOperations.");
+ }
+ op = null;
+ mCurrentOperations.delete(token);
+ } else if (state == OP_PENDING) {
+ if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
+ op.state = OP_TIMEOUT;
+ // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
+ // called after we receive cancel here. We need this op's state there.
+
+ // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
+ // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+ // doesn't require cancellation.
+ if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+ }
+ }
+ mCurrentOpLock.notifyAll();
+ }
+
+ // If there's a TimeoutHandler for this event, call it
+ if (op != null && op.callback != null) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, " Invoking cancel on " + op.callback);
+ }
+ op.callback.handleCancel(cancelAll);
+ }
+ }
+
+ /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
+ public boolean isBackupOperationInProgress() {
+ synchronized (mCurrentOpLock) {
+ for (int i = 0; i < mCurrentOperations.size(); i++) {
+ Operation op = mCurrentOperations.valueAt(i);
+ if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Unbind the backup agent and kill the app if it's a non-system app. */
+ public void tearDownAgentAndKill(ApplicationInfo app) {
+ if (app == null) {
+ // Null means the system package, so just quietly move on. :)
+ return;
+ }
+
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
+
+ // The agent was running with a stub Application object, so shut it down.
+ // !!! We hardcode the confirmation UI's package name here rather than use a
+ // manifest flag! TODO something less direct.
+ if (app.uid >= Process.FIRST_APPLICATION_UID
+ && !app.packageName.equals("com.android.backupconfirm")) {
+ if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
+ }
+
+ /** For adb backup/restore. */
+ public boolean deviceIsEncrypted() {
+ try {
+ return mStorageManager.getEncryptionState()
+ != StorageManager.ENCRYPTION_STATE_NONE
+ && mStorageManager.getPasswordType()
+ != StorageManager.CRYPT_TYPE_DEFAULT;
+ } catch (Exception e) {
+ // If we can't talk to the storagemanager service we have a serious problem; fail
+ // "secure" i.e. assuming that the device is encrypted.
+ Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
+ return true;
+ }
+ }
+
+ // ----- Full-data backup scheduling -----
+
+ /**
+ * Schedule a job to tell us when it's a good time to run a full backup
+ */
+ public void scheduleNextFullBackupJob(long transportMinLatency) {
+ synchronized (mQueueLock) {
+ if (mFullBackupQueue.size() > 0) {
+ // schedule the next job at the point in the future when the least-recently
+ // backed up app comes due for backup again; or immediately if it's already
+ // due.
+ final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
+ final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
+ final long interval = mConstants.getFullBackupIntervalMilliseconds();
+ final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
+ final long latency = Math.max(transportMinLatency, appLatency);
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ FullBackupJob.schedule(mContext, latency, mConstants);
+ }
+ };
+ mBackupHandler.postDelayed(r, 2500);
+ } else {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Full backup queue empty; not scheduling");
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove a package from the full-data queue.
+ */
+ @GuardedBy("mQueueLock")
+ private void dequeueFullBackupLocked(String packageName) {
+ final int numPackages = mFullBackupQueue.size();
+ for (int i = numPackages - 1; i >= 0; i--) {
+ final FullBackupEntry e = mFullBackupQueue.get(i);
+ if (packageName.equals(e.packageName)) {
+ mFullBackupQueue.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Enqueue full backup for the given app, with a note about when it last ran.
+ */
+ public void enqueueFullBackup(String packageName, long lastBackedUp) {
+ FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
+ synchronized (mQueueLock) {
+ // First, sanity check that we aren't adding a duplicate. Slow but
+ // straightforward; we'll have at most on the order of a few hundred
+ // items in this list.
+ dequeueFullBackupLocked(packageName);
+
+ // This is also slow but easy for modest numbers of apps: work backwards
+ // from the end of the queue until we find an item whose last backup
+ // time was before this one, then insert this new entry after it. If we're
+ // adding something new we don't bother scanning, and just prepend.
+ int which = -1;
+ if (lastBackedUp > 0) {
+ for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
+ final FullBackupEntry entry = mFullBackupQueue.get(which);
+ if (entry.lastBackup <= lastBackedUp) {
+ mFullBackupQueue.add(which + 1, newEntry);
+ break;
+ }
+ }
+ }
+ if (which < 0) {
+ // this one is earlier than any existing one, so prepend
+ mFullBackupQueue.add(0, newEntry);
+ }
+ }
+ writeFullBackupScheduleAsync();
+ }
+
+ private boolean fullBackupAllowable(String transportName) {
+ if (!mTransportManager.isTransportRegistered(transportName)) {
+ Slog.w(TAG, "Transport not registered; full data backup not performed");
+ return false;
+ }
+
+ // Don't proceed unless we have already established package metadata
+ // for the current dataset via a key/value backup pass.
+ try {
+ String transportDirName = mTransportManager.getTransportDirName(transportName);
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+ if (pmState.length() <= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+ }
+ return false;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Conditions are right for a full backup operation, so run one. The model we use is
+ * to perform one app backup per scheduled job execution, and to reschedule the job
+ * with zero latency as long as conditions remain right and we still have work to do.
+ *
+ * <p>This is the "start a full backup operation" entry point called by the scheduled job.
+ *
+ * @return Whether ongoing work will continue. The return value here will be passed
+ * along as the return value to the scheduled job's onStartJob() callback.
+ */
+ public boolean beginFullBackup(FullBackupJob scheduledJob) {
+ final long now = System.currentTimeMillis();
+ final long fullBackupInterval;
+ final long keyValueBackupInterval;
+ synchronized (mConstants) {
+ fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
+ keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
+ }
+ FullBackupEntry entry = null;
+ long latency = fullBackupInterval;
+
+ if (!mEnabled || !mProvisioned) {
+ // Backups are globally disabled, so don't proceed. We also don't reschedule
+ // the job driving automatic backups; that job will be scheduled again when
+ // the user enables backup.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+ + " p=" + mProvisioned + "; ignoring");
+ }
+ return false;
+ }
+
+ // Don't run the backup if we're in battery saver mode, but reschedule
+ // to try again in the not-so-distant future.
+ final PowerSaveState result =
+ mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
+ if (result.batterySaverEnabled) {
+ if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
+ FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+ return false;
+ }
+
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Beginning scheduled full backup operation");
+ }
+
+ // Great; we're able to run full backup jobs now. See if we have any work to do.
+ synchronized (mQueueLock) {
+ if (mRunningFullBackupTask != null) {
+ Slog.e(TAG, "Backup triggered but one already/still running!");
+ return false;
+ }
+
+ // At this point we think that we have work to do, but possibly not right now.
+ // Any exit without actually running backups will also require that we
+ // reschedule the job.
+ boolean runBackup = true;
+ boolean headBusy;
+
+ do {
+ // Recheck each time, because culling due to ineligibility may
+ // have emptied the queue.
+ if (mFullBackupQueue.size() == 0) {
+ // no work to do so just bow out
+ if (DEBUG) {
+ Slog.i(TAG, "Backup queue empty; doing nothing");
+ }
+ runBackup = false;
+ break;
+ }
+
+ headBusy = false;
+
+ String transportName = mTransportManager.getCurrentTransportName();
+ if (!fullBackupAllowable(transportName)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Preconditions not met; not running full backup");
+ }
+ runBackup = false;
+ // Typically this means we haven't run a key/value backup yet. Back off
+ // full-backup operations by the key/value job's run interval so that
+ // next time we run, we are likely to be able to make progress.
+ latency = keyValueBackupInterval;
+ }
+
+ if (runBackup) {
+ entry = mFullBackupQueue.get(0);
+ long timeSinceRun = now - entry.lastBackup;
+ runBackup = (timeSinceRun >= fullBackupInterval);
+ if (!runBackup) {
+ // It's too early to back up the next thing in the queue, so bow out
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Device ready but too early to back up next app");
+ }
+ // Wait until the next app in the queue falls due for a full data backup
+ latency = fullBackupInterval - timeSinceRun;
+ break; // we know we aren't doing work yet, so bail.
+ }
+
+ try {
+ PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
+ // The head app isn't supposed to get full-data backups [any more];
+ // so we cull it and force a loop around to consider the new head
+ // app.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Culling package " + entry.packageName
+ + " in full-backup queue but not eligible");
+ }
+ mFullBackupQueue.remove(0);
+ headBusy = true; // force the while() condition
+ continue;
+ }
+
+ final int privFlags = appInfo.applicationInfo.privateFlags;
+ headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
+ && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+ if (headBusy) {
+ final long nextEligible = System.currentTimeMillis()
+ + BUSY_BACKOFF_MIN_MILLIS
+ + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+ if (DEBUG_SCHEDULING) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Slog.i(TAG, "Full backup time but " + entry.packageName
+ + " is busy; deferring to "
+ + sdf.format(new Date(nextEligible)));
+ }
+ // This relocates the app's entry from the head of the queue to
+ // its order-appropriate position further down, so upon looping
+ // a new candidate will be considered at the head.
+ enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
+ }
+ } catch (NameNotFoundException nnf) {
+ // So, we think we want to back this up, but it turns out the package
+ // in question is no longer installed. We want to drop it from the
+ // queue entirely and move on, but if there's nothing else in the queue
+ // we should bail entirely. headBusy cannot have been set to true yet.
+ runBackup = (mFullBackupQueue.size() > 1);
+ } catch (RemoteException e) {
+ // Cannot happen; the Activity Manager is in the same process
+ }
+ }
+ } while (headBusy);
+
+ if (!runBackup) {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
+ }
+ final long deferTime = latency; // pin for the closure
+ mBackupHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ FullBackupJob.schedule(mContext, deferTime, mConstants);
+ }
+ });
+ return false;
+ }
+
+ // Okay, the top thing is ready for backup now. Do it.
+ mFullBackupQueue.remove(0);
+ CountDownLatch latch = new CountDownLatch(1);
+ String[] pkg = new String[]{entry.packageName};
+ mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ /* observer */ null,
+ pkg,
+ /* updateSchedule */ true,
+ scheduledJob,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.beginFullBackup()");
+ // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+ mWakelock.acquire();
+ (new Thread(mRunningFullBackupTask)).start();
+ }
+
+ return true;
+ }
+
+ /**
+ * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
+ * task right away.
+ */
+ public void endFullBackup() {
+ // offload the mRunningFullBackupTask.handleCancel() call to another thread,
+ // as we might have to wait for mCancelLock
+ Runnable endFullBackupRunnable = new Runnable() {
+ @Override
+ public void run() {
+ PerformFullTransportBackupTask pftbt = null;
+ synchronized (mQueueLock) {
+ if (mRunningFullBackupTask != null) {
+ pftbt = mRunningFullBackupTask;
+ }
+ }
+ if (pftbt != null) {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Telling running backup to stop");
+ }
+ pftbt.handleCancel(true);
+ }
+ }
+ };
+ new Thread(endFullBackupRunnable, "end-full-backup").start();
+ }
+
+ /** Used by both incremental and full restore to restore widget data. */
+ public void restoreWidgetData(String packageName, byte[] widgetData) {
+ // Apply the restored widget state and generate the ID update for the app
+ // TODO: http://b/22388012
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Incorporating restored widget data");
+ }
+ AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
+ }
+
+ // *****************************
+ // NEW UNIFIED RESTORE IMPLEMENTATION
+ // *****************************
+
+ /** Schedule a backup pass for {@code packageName}. */
+ public void dataChangedImpl(String packageName) {
+ HashSet<String> targets = dataChangedTargets(packageName);
+ dataChangedImpl(packageName, targets);
+ }
+
+ private void dataChangedImpl(String packageName, HashSet<String> targets) {
+ // Record that we need a backup pass for the caller. Since multiple callers
+ // may share a uid, we need to note all candidates within that uid and schedule
+ // a backup pass for each of them.
+ if (targets == null) {
+ Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ // Note that this client has made data changes that need to be backed up
+ if (targets.contains(packageName)) {
+ // Add the caller to the set of pending backups. If there is
+ // one already there, then overwrite it, but no harm done.
+ BackupRequest req = new BackupRequest(packageName);
+ if (mPendingBackups.put(packageName, req) == null) {
+ if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+
+ // Journal this request in case of crash. The put()
+ // operation returned null when this package was not already
+ // in the set; we want to avoid touching the disk redundantly.
+ writeToJournalLocked(packageName);
+ }
+ }
+ }
+
+ // ...and schedule a backup pass if necessary
+ KeyValueBackupJob.schedule(mContext, mConstants);
+ }
+
+ // Note: packageName is currently unused, but may be in the future
+ private HashSet<String> dataChangedTargets(String packageName) {
+ // If the caller does not hold the BACKUP permission, it can only request a
+ // backup of its own data.
+ if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+ Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+ synchronized (mBackupParticipants) {
+ return mBackupParticipants.get(Binder.getCallingUid());
+ }
+ }
+
+ // a caller with full permission can ask to back up any participating app
+ if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+ return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
+ } else {
+ synchronized (mBackupParticipants) {
+ return SparseArrayUtils.union(mBackupParticipants);
+ }
+ }
+ }
+
+ private void writeToJournalLocked(String str) {
+ try {
+ if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
+ mJournal.addPackage(str);
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't write " + str + " to backup journal", e);
+ mJournal = null;
+ }
+ }
+
+ // ----- IBackupManager binder interface -----
+
+ /** Sent from an app's backup agent to let the service know that there's new data to backup. */
+ public void dataChanged(final String packageName) {
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ // TODO: http://b/22388012
+ // App is running under a non-owner user profile. For now, we do not back
+ // up data from secondary user profiles.
+ // TODO: backups for all user profiles although don't add backup for profiles
+ // without adding admin control in DevicePolicyManager.
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+ + callingUserHandle);
+ }
+ return;
+ }
+
+ final HashSet<String> targets = dataChangedTargets(packageName);
+ if (targets == null) {
+ Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ mBackupHandler.post(new Runnable() {
+ public void run() {
+ dataChangedImpl(packageName, targets);
+ }
+ });
+ }
+
+ /** Run an initialize operation for the given transport. */
+ public void initializeTransports(String[] transportNames, IBackupObserver observer) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "initializeTransport");
+ Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ mWakelock.acquire();
+ OnTaskFinishedListener listener = caller -> mWakelock.release();
+ mBackupHandler.post(
+ new PerformInitializeTask(this, transportNames, observer, listener));
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Clear the given package's backup data from the current transport. */
+ public void clearBackupData(String transportName, String packageName) {
+ if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
+ PackageInfo info;
+ try {
+ info = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ } catch (NameNotFoundException e) {
+ Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+ return;
+ }
+
+ // If the caller does not hold the BACKUP permission, it can only request a
+ // wipe of its own backed-up data.
+ Set<String> apps;
+ if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+ Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+ apps = mBackupParticipants.get(Binder.getCallingUid());
+ } else {
+ // a caller with full permission can ask to back up any participating app
+ // !!! TODO: allow data-clear of ANY app?
+ if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
+ apps = mProcessedPackagesJournal.getPackagesCopy();
+ }
+
+ if (apps.contains(packageName)) {
+ // found it; fire off the clear request
+ if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
+ mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
+ synchronized (mQueueLock) {
+ TransportClient transportClient =
+ mTransportManager
+ .getTransportClient(transportName, "BMS.clearBackupData()");
+ if (transportClient == null) {
+ // transport is currently unregistered -- make sure to retry
+ Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
+ new ClearRetryParams(transportName, packageName));
+ mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
+ return;
+ }
+ long oldId = Binder.clearCallingIdentity();
+ OnTaskFinishedListener listener =
+ caller ->
+ mTransportManager.disposeOfTransportClient(transportClient, caller);
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(
+ MSG_RUN_CLEAR,
+ new ClearParams(transportClient, info, listener));
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+ }
+
+ /**
+ * Run a backup pass immediately for any applications that have declared that they have pending
+ * updates.
+ */
+ public void backupNow() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ final PowerSaveState result =
+ mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
+ if (result.batterySaverEnabled) {
+ if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
+ KeyValueBackupJob.schedule(mContext, mConstants); // try again in several hours
+ } else {
+ if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
+ synchronized (mQueueLock) {
+ // Fire the intent that kicks off the whole shebang...
+ try {
+ mRunBackupIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ // should never happen
+ Slog.e(TAG, "run-backup intent cancelled!");
+ }
+
+ // ...and cancel any pending scheduled job, because we've just superseded it
+ KeyValueBackupJob.cancel(mContext);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Returns {@code true} if the system user has gone through SUW. */
+ public boolean deviceIsProvisioned() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+ }
+
+ /**
+ * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
+ * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
+ * return to the caller until the backup has been completed. It requires on-screen confirmation
+ * by the user.
+ */
+ public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+ boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
+ boolean compress, boolean doKeyValue, String[] pkgList) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
+
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ throw new IllegalStateException("Backup supported only for the device owner");
+ }
+
+ // Validate
+ if (!doAllApps) {
+ if (!includeShared) {
+ // If we're backing up shared data (sdcard or equivalent), then we can run
+ // without any supplied app names. Otherwise, we'd be doing no work, so
+ // report the error.
+ if (pkgList == null || pkgList.length == 0) {
+ throw new IllegalArgumentException(
+ "Backup requested but neither shared nor any apps named");
+ }
+ }
+ }
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ // Doesn't make sense to do a full backup prior to setup
+ if (!deviceIsProvisioned()) {
+ Slog.i(TAG, "Backup not supported before setup");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
+ + " shared=" + includeShared + " all=" + doAllApps + " system="
+ + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
+ }
+ Slog.i(TAG, "Beginning adb backup...");
+
+ AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
+ includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
+ pkgList);
+ final int token = generateRandomIntegerToken();
+ synchronized (mAdbBackupRestoreConfirmations) {
+ mAdbBackupRestoreConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI
+ if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+ if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+ Slog.e(TAG, "Unable to launch backup confirmation UI");
+ mAdbBackupRestoreConfirmations.delete(token);
+ return;
+ }
+
+ // make sure the screen is lit for the user interaction
+ mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0);
+
+ // start the confirmation countdown
+ startConfirmationTimeout(token, params);
+
+ // wait for the backup to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
+ waitForCompletion(params);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
+ }
+ Binder.restoreCallingIdentity(oldId);
+ Slog.d(TAG, "Adb backup processing complete.");
+ }
+ }
+
+ /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
+ public void fullTransportBackup(String[] pkgNames) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "fullTransportBackup");
+
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ throw new IllegalStateException("Restore supported only for the device owner");
+ }
+
+ String transportName = mTransportManager.getCurrentTransportName();
+ if (!fullBackupAllowable(transportName)) {
+ Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "fullTransportBackup()");
+ }
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ CountDownLatch latch = new CountDownLatch(1);
+ Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ /* observer */ null,
+ pkgNames,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.fullTransportBackup()");
+ // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+ mWakelock.acquire();
+ (new Thread(task, "full-transport-master")).start();
+ do {
+ try {
+ latch.await();
+ break;
+ } catch (InterruptedException e) {
+ // Just go back to waiting for the latch to indicate completion
+ }
+ } while (true);
+
+ // We just ran a backup on these packages, so kick them to the end of the queue
+ final long now = System.currentTimeMillis();
+ for (String pkg : pkgNames) {
+ enqueueFullBackup(pkg, now);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Done with full transport backup.");
+ }
+ }
+
+ /**
+ * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
+ * confirmation.
+ */
+ public void adbRestore(ParcelFileDescriptor fd) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
+
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ throw new IllegalStateException("Restore supported only for the device owner");
+ }
+
+ long oldId = Binder.clearCallingIdentity();
+
+ try {
+ // Check whether the device has been provisioned -- we don't handle
+ // full restores prior to completing the setup process.
+ if (!deviceIsProvisioned()) {
+ Slog.i(TAG, "Full restore not permitted before setup");
+ return;
+ }
+
+ Slog.i(TAG, "Beginning restore...");
+
+ AdbRestoreParams params = new AdbRestoreParams(fd);
+ final int token = generateRandomIntegerToken();
+ synchronized (mAdbBackupRestoreConfirmations) {
+ mAdbBackupRestoreConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI
+ if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+ if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+ Slog.e(TAG, "Unable to launch restore confirmation");
+ mAdbBackupRestoreConfirmations.delete(token);
+ return;
+ }
+
+ // make sure the screen is lit for the user interaction
+ mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0);
+
+ // start the confirmation countdown
+ startConfirmationTimeout(token, params);
+
+ // wait for the restore to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
+ waitForCompletion(params);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
+ }
+ Binder.restoreCallingIdentity(oldId);
+ Slog.i(TAG, "adb restore processing complete.");
+ }
+ }
+
+ private boolean startConfirmationUi(int token, String action) {
+ try {
+ Intent confIntent = new Intent(action);
+ confIntent.setClassName("com.android.backupconfirm",
+ "com.android.backupconfirm.BackupRestoreConfirmation");
+ confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+ confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
+ } catch (ActivityNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private void startConfirmationTimeout(int token, AdbParams params) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION + " millis");
+ }
+ Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+ token, 0, params);
+ mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+ }
+
+ private void waitForCompletion(AdbParams params) {
+ synchronized (params.latch) {
+ while (!params.latch.get()) {
+ try {
+ params.latch.wait();
+ } catch (InterruptedException e) { /* never interrupted */ }
+ }
+ }
+ }
+
+ /** Called when adb backup/restore has completed. */
+ public void signalAdbBackupRestoreCompletion(AdbParams params) {
+ synchronized (params.latch) {
+ params.latch.set(true);
+ params.latch.notifyAll();
+ }
+ }
+
+ /**
+ * Confirm that the previously-requested full backup/restore operation can proceed. This is used
+ * to require a user-facing disclosure about the operation.
+ */
+ public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
+ String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
+ if (DEBUG) {
+ Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
+ + " allow=" + allow);
+ }
+
+ // TODO: possibly require not just this signature-only permission, but even
+ // require that the specific designated confirmation-UI app uid is the caller?
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "acknowledgeAdbBackupOrRestore");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+
+ AdbParams params;
+ synchronized (mAdbBackupRestoreConfirmations) {
+ params = mAdbBackupRestoreConfirmations.get(token);
+ if (params != null) {
+ mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+ mAdbBackupRestoreConfirmations.delete(token);
+
+ if (allow) {
+ final int verb = params instanceof AdbBackupParams
+ ? MSG_RUN_ADB_BACKUP
+ : MSG_RUN_ADB_RESTORE;
+
+ params.observer = observer;
+ params.curPassword = curPassword;
+
+ params.encryptPassword = encPpassword;
+
+ if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(verb, params);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ Slog.w(TAG, "User rejected full backup/restore operation");
+ // indicate completion without having actually transferred any data
+ signalAdbBackupRestoreCompletion(params);
+ }
+ } else {
+ Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** User-configurable enabling/disabling of backups. */
+ public void setBackupEnabled(boolean enable) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setBackupEnabled");
+
+ Slog.i(TAG, "Backup enabled => " + enable);
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ boolean wasEnabled = mEnabled;
+ synchronized (this) {
+ // TODO(b/118520567): Clean up writing backup enabled logic.
+ BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
+ mEnabled = enable;
+ }
+
+ synchronized (mQueueLock) {
+ if (enable && !wasEnabled && mProvisioned) {
+ // if we've just been enabled, start scheduling backup passes
+ KeyValueBackupJob.schedule(mContext, mConstants);
+ scheduleNextFullBackupJob(0);
+ } else if (!enable) {
+ // No longer enabled, so stop running backups
+ if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
+
+ KeyValueBackupJob.cancel(mContext);
+
+ // This also constitutes an opt-out, so we wipe any data for
+ // this device from the backend. We start that process with
+ // an alarm in order to guarantee wakelock states.
+ if (wasEnabled && mProvisioned) {
+ // NOTE: we currently flush every registered transport, not just
+ // the currently-active one.
+ List<String> transportNames = new ArrayList<>();
+ List<String> transportDirNames = new ArrayList<>();
+ mTransportManager.forEachRegisteredTransport(
+ name -> {
+ final String dirName;
+ try {
+ dirName =
+ mTransportManager
+ .getTransportDirName(name);
+ } catch (TransportNotRegisteredException e) {
+ // Should never happen
+ Slog.e(TAG, "Unexpected unregistered transport", e);
+ return;
+ }
+ transportNames.add(name);
+ transportDirNames.add(dirName);
+ });
+
+ // build the set of transports for which we are posting an init
+ for (int i = 0; i < transportNames.size(); i++) {
+ recordInitPending(
+ true,
+ transportNames.get(i),
+ transportDirNames.get(i));
+ }
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
+ mRunInitIntent);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Enable/disable automatic restore of app data at install time. */
+ public void setAutoRestore(boolean doAutoRestore) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setAutoRestore");
+
+ Slog.i(TAG, "Auto restore => " + doAutoRestore);
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+ mAutoRestore = doAutoRestore;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Mark the backup service as having been provisioned. */
+ public void setBackupProvisioned(boolean available) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setBackupProvisioned");
+ /*
+ * This is now a no-op; provisioning is simply the device's own setup state.
+ */
+ }
+
+ /** Report whether the backup mechanism is currently enabled. */
+ public boolean isBackupEnabled() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "isBackupEnabled");
+ return mEnabled; // no need to synchronize just to read it
+ }
+
+ /** Report the name of the currently active transport. */
+ public String getCurrentTransport() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getCurrentTransport");
+ String currentTransport = mTransportManager.getCurrentTransportName();
+ if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
+ return currentTransport;
+ }
+
+ /**
+ * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+ * null} if no transport selected or if the transport selected is not registered.
+ */
+ @Nullable
+ public ComponentName getCurrentTransportComponent() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ return mTransportManager.getCurrentTransportComponent();
+ } catch (TransportNotRegisteredException e) {
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Report all known, available backup transports by name. */
+ public String[] listAllTransports() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "listAllTransports");
+
+ return mTransportManager.getRegisteredTransportNames();
+ }
+
+ /** Report all known, available backup transports by component. */
+ public ComponentName[] listAllTransportComponents() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "listAllTransportComponents");
+ return mTransportManager.getRegisteredTransportComponents();
+ }
+
+ /** Report all system whitelisted transports. */
+ public String[] getTransportWhitelist() {
+ // No permission check, intentionally.
+ Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
+ String[] whitelistedTransports = new String[whitelistedComponents.size()];
+ int i = 0;
+ for (ComponentName component : whitelistedComponents) {
+ whitelistedTransports[i] = component.flattenToShortString();
+ i++;
+ }
+ return whitelistedTransports;
+ }
+
+ /**
+ * Update the attributes of the transport identified by {@code transportComponent}. If the
+ * specified transport has not been bound at least once (for registration), this call will be
+ * ignored. Only the host process of the transport can change its description, otherwise a
+ * {@link SecurityException} will be thrown.
+ *
+ * @param transportComponent The identity of the transport being described.
+ * @param name A {@link String} with the new name for the transport. This is NOT for
+ * identification. MUST NOT be {@code null}.
+ * @param configurationIntent An {@link Intent} that can be passed to
+ * {@link Context#startActivity} in order to launch the transport's configuration UI. It may
+ * be {@code null} if the transport does not offer any user-facing configuration UI.
+ * @param currentDestinationString A {@link String} describing the destination to which the
+ * transport is currently sending data. MUST NOT be {@code null}.
+ * @param dataManagementIntent An {@link Intent} that can be passed to
+ * {@link Context#startActivity} in order to launch the transport's data-management UI. It
+ * may be {@code null} if the transport does not offer any user-facing data
+ * management UI.
+ * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
+ * management affordance. This MUST be {@code null} when dataManagementIntent is
+ * {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+ * @throws SecurityException If the UID of the calling process differs from the package UID of
+ * {@code transportComponent} or if the caller does NOT have BACKUP permission.
+ */
+ public void updateTransportAttributes(
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ @Nullable String dataManagementLabel) {
+ updateTransportAttributes(
+ Binder.getCallingUid(),
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+
+ @VisibleForTesting
+ void updateTransportAttributes(
+ int callingUid,
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ @Nullable String dataManagementLabel) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "updateTransportAttributes");
+
+ Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
+ Preconditions.checkNotNull(name, "name can't be null");
+ Preconditions.checkNotNull(
+ currentDestinationString, "currentDestinationString can't be null");
+ Preconditions.checkArgument(
+ (dataManagementIntent == null) == (dataManagementLabel == null),
+ "dataManagementLabel should be null iff dataManagementIntent is null");
+
+ try {
+ int transportUid =
+ mContext.getPackageManager()
+ .getPackageUid(transportComponent.getPackageName(), 0);
+ if (callingUid != transportUid) {
+ throw new SecurityException("Only the transport can change its description");
+ }
+ } catch (NameNotFoundException e) {
+ throw new SecurityException("Transport package not found", e);
+ }
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ mTransportManager.updateTransportAttributes(
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /**
+ * Selects transport {@code transportName} and returns previously selected transport.
+ *
+ * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public String selectBackupTransport(String transportName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "selectBackupTransport");
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ String previousTransportName = mTransportManager.selectTransport(transportName);
+ updateStateForTransport(transportName);
+ Slog.v(TAG, "selectBackupTransport(transport = " + transportName
+ + "): previous transport = " + previousTransportName);
+ return previousTransportName;
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /**
+ * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
+ * with the result upon completion.
+ */
+ public void selectBackupTransportAsync(
+ ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ String transportString = transportComponent.flattenToShortString();
+ Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
+ mBackupHandler.post(
+ () -> {
+ String transportName = null;
+ int result =
+ mTransportManager.registerAndSelectTransport(transportComponent);
+ if (result == BackupManager.SUCCESS) {
+ try {
+ transportName =
+ mTransportManager.getTransportName(transportComponent);
+ updateStateForTransport(transportName);
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Transport got unregistered");
+ result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+ }
+ }
+
+ try {
+ if (transportName != null) {
+ listener.onSuccess(transportName);
+ } else {
+ listener.onFailure(result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ private void updateStateForTransport(String newTransportName) {
+ // Publish the name change
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+
+ // And update our current-dataset bookkeeping
+ String callerLogString = "BMS.updateStateForTransport()";
+ TransportClient transportClient =
+ mTransportManager.getTransportClient(newTransportName, callerLogString);
+ if (transportClient != null) {
+ try {
+ IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
+ mCurrentToken = transport.getCurrentRestoreSet();
+ } catch (Exception e) {
+ // Oops. We can't know the current dataset token, so reset and figure it out
+ // when we do the next k/v backup operation on this transport.
+ mCurrentToken = 0;
+ Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
+ }
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ } else {
+ Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
+ // The named transport isn't registered, so we can't know what its current dataset token
+ // is. Reset as above.
+ mCurrentToken = 0;
+ }
+ }
+
+ /**
+ * Supply the configuration intent for the given transport. If the name is not one of the
+ * available transports, or if the transport does not supply any configuration UI, the method
+ * returns {@code null}.
+ */
+ public Intent getConfigurationIntent(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getConfigurationIntent");
+ try {
+ Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
+ }
+ return intent;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Supply the current destination string for the given transport. If the name is not one of the
+ * registered transports the method will return null.
+ *
+ * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
+ *
+ * @param transportName The name of the registered transport.
+ * @return The current destination string or null if the transport is not registered.
+ */
+ public String getDestinationString(String transportName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getDestinationString");
+
+ try {
+ String string = mTransportManager.getTransportCurrentDestinationString(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDestinationString() returning " + string);
+ }
+ return string;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /** Supply the manage-data intent for the given transport. */
+ public Intent getDataManagementIntent(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getDataManagementIntent");
+
+ try {
+ Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
+ }
+ return intent;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Supply the menu label for affordances that fire the manage-data intent for the given
+ * transport.
+ */
+ public String getDataManagementLabel(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getDataManagementLabel");
+
+ try {
+ String label = mTransportManager.getTransportDataManagementLabel(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDataManagementLabel() returning " + label);
+ }
+ return label;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Callback: a requested backup agent has been instantiated. This should only be called from the
+ * {@link ActivityManager}.
+ */
+ public void agentConnected(String packageName, IBinder agentBinder) {
+ synchronized (mAgentConnectLock) {
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+ IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
+ mConnectedAgent = agent;
+ mConnecting = false;
+ } else {
+ Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " claiming agent connected");
+ }
+ mAgentConnectLock.notifyAll();
+ }
+ }
+
+ /**
+ * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
+ * to come up in the first place, the agentBinder argument will be {@code null}. This should
+ * only be called from the {@link ActivityManager}.
+ */
+ public void agentDisconnected(String packageName) {
+ // TODO: handle backup being interrupted
+ synchronized (mAgentConnectLock) {
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ mConnectedAgent = null;
+ mConnecting = false;
+ } else {
+ Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " claiming agent disconnected");
+ }
+ mAgentConnectLock.notifyAll();
+ }
+ }
+
+ /**
+ * An application being installed will need a restore pass, then the {@link PackageManager} will
+ * need to be told when the restore is finished.
+ */
+ public void restoreAtInstall(String packageName, int token) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " attemping install-time restore");
+ return;
+ }
+
+ boolean skip = false;
+
+ long restoreSet = getAvailableRestoreToken(packageName);
+ if (DEBUG) {
+ Slog.v(TAG, "restoreAtInstall pkg=" + packageName
+ + " token=" + Integer.toHexString(token)
+ + " restoreSet=" + Long.toHexString(restoreSet));
+ }
+ if (restoreSet == 0) {
+ if (MORE_DEBUG) Slog.i(TAG, "No restore set");
+ skip = true;
+ }
+
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
+ if (transportClient == null) {
+ if (DEBUG) Slog.w(TAG, "No transport client");
+ skip = true;
+ }
+
+ if (!mAutoRestore) {
+ if (DEBUG) {
+ Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
+ }
+ skip = true;
+ }
+
+ if (!skip) {
+ try {
+ // okay, we're going to attempt a restore of this package from this restore set.
+ // The eventual message back into the Package Manager to run the post-install
+ // steps for 'token' will be issued from the restore handling code.
+
+ mWakelock.acquire();
+
+ OnTaskFinishedListener listener = caller -> {
+ mTransportManager.disposeOfTransportClient(transportClient, caller);
+ mWakelock.release();
+ };
+
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Restore at install of " + packageName);
+ }
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj =
+ RestoreParams.createForRestoreAtInstall(
+ transportClient,
+ /* observer */ null,
+ /* monitor */ null,
+ restoreSet,
+ packageName,
+ token,
+ listener);
+ mBackupHandler.sendMessage(msg);
+ } catch (Exception e) {
+ // Calling into the transport broke; back off and proceed with the installation.
+ Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
+ skip = true;
+ }
+ }
+
+ if (skip) {
+ // Auto-restore disabled or no way to attempt a restore
+
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(
+ transportClient, "BMS.restoreAtInstall()");
+ }
+
+ // Tell the PackageManager to proceed with the post-install handling for this package.
+ if (DEBUG) Slog.v(TAG, "Finishing install immediately");
+ try {
+ mPackageManagerBinder.finishPackageInstall(token, false);
+ } catch (RemoteException e) { /* can't happen */ }
+ }
+ }
+
+ /** Hand off a restore session. */
+ public IRestoreSession beginRestoreSession(String packageName, String transport) {
+ if (DEBUG) {
+ Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
+ + " transport=" + transport);
+ }
+
+ boolean needPermission = true;
+ if (transport == null) {
+ transport = mTransportManager.getCurrentTransportName();
+
+ if (packageName != null) {
+ PackageInfo app = null;
+ try {
+ app = mPackageManager.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException nnf) {
+ Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+ throw new IllegalArgumentException("Package " + packageName + " not found");
+ }
+
+ if (app.applicationInfo.uid == Binder.getCallingUid()) {
+ // So: using the current active transport, and the caller has asked
+ // that its own package will be restored. In this narrow use case
+ // we do not require the caller to hold the permission.
+ needPermission = false;
+ }
+ }
+ }
+
+ if (needPermission) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "beginRestoreSession");
+ } else {
+ if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
+ }
+
+ synchronized (this) {
+ if (mActiveRestoreSession != null) {
+ Slog.i(TAG, "Restore session requested but one already active");
+ return null;
+ }
+ if (mBackupRunning) {
+ Slog.i(TAG, "Restore session requested but currently running backups");
+ return null;
+ }
+ mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
+ }
+ return mActiveRestoreSession;
+ }
+
+ /** Clear the specified restore session. */
+ public void clearRestoreSession(ActiveRestoreSession currentSession) {
+ synchronized (this) {
+ if (currentSession != mActiveRestoreSession) {
+ Slog.e(TAG, "ending non-current restore session");
+ } else {
+ if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
+ mActiveRestoreSession = null;
+ mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+ }
+ }
+ }
+
+ /**
+ * Note that a currently-active backup agent has notified us that it has completed the given
+ * outstanding asynchronous backup/restore operation.
+ */
+ public void opComplete(int token, long result) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+ }
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ op = mCurrentOperations.get(token);
+ if (op != null) {
+ if (op.state == OP_TIMEOUT) {
+ // The operation already timed out, and this is a late response. Tidy up
+ // and ignore it; we've already dealt with the timeout.
+ op = null;
+ mCurrentOperations.delete(token);
+ } else if (op.state == OP_ACKNOWLEDGED) {
+ if (DEBUG) {
+ Slog.w(TAG, "Received duplicate ack for token="
+ + Integer.toHexString(token));
+ }
+ op = null;
+ mCurrentOperations.remove(token);
+ } else if (op.state == OP_PENDING) {
+ // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
+ // called after we we receive this call.
+ op.state = OP_ACKNOWLEDGED;
+ }
+ }
+ mCurrentOpLock.notifyAll();
+ }
+
+ // The completion callback, if any, is invoked on the handler
+ if (op != null && op.callback != null) {
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
+ mBackupHandler.sendMessage(msg);
+ }
+ }
+
+ /** Checks if the package is eligible for backup. */
+ public boolean isAppEligibleForBackup(String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
+
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.isAppEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ boolean eligible =
+ AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager);
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligible;
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
+ }
+ }
+
+ /** Returns the inputted packages that are eligible for backup. */
+ public String[] filterAppsEligibleForBackup(String[] packages) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.filterAppsEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ List<String> eligibleApps = new LinkedList<>();
+ for (String packageName : packages) {
+ if (AppBackupUtils
+ .appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager)) {
+ eligibleApps.add(packageName);
+ }
+ }
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligibleApps.toArray(new String[eligibleApps.size()]);
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
+ }
+ }
+
+ /** Prints service state for 'dumpsys backup'. */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ if (args != null) {
+ for (String arg : args) {
+ if ("-h".equals(arg)) {
+ pw.println("'dumpsys backup' optional arguments:");
+ pw.println(" -h : this help text");
+ pw.println(" a[gents] : dump information about defined backup agents");
+ return;
+ } else if ("agents".startsWith(arg)) {
+ dumpAgents(pw);
+ return;
+ } else if ("transportclients".equals(arg.toLowerCase())) {
+ mTransportManager.dumpTransportClients(pw);
+ return;
+ } else if ("transportstats".equals(arg.toLowerCase())) {
+ mTransportManager.dumpTransportStats(pw);
+ return;
+ }
+ }
+ }
+ dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void dumpAgents(PrintWriter pw) {
+ List<PackageInfo> agentPackages = allAgentPackages();
+ pw.println("Defined backup agents:");
+ for (PackageInfo pkg : agentPackages) {
+ pw.print(" ");
+ pw.print(pkg.packageName);
+ pw.println(':');
+ pw.print(" ");
+ pw.println(pkg.applicationInfo.backupAgentName);
+ }
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ synchronized (mQueueLock) {
+ pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+ + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
+ pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
+ if (mBackupRunning) pw.println("Backup currently running");
+ pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
+ pw.println("Last backup pass started: " + mLastBackupPass
+ + " (now = " + System.currentTimeMillis() + ')');
+ pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
+
+ pw.println("Transport whitelist:");
+ for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
+ pw.print(" ");
+ pw.println(transport.flattenToShortString());
+ }
+
+ pw.println("Available transports:");
+ final String[] transports = listAllTransports();
+ if (transports != null) {
+ for (String t : transports) {
+ pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * "
+ : " ") + t);
+ try {
+ File dir = new File(mBaseStateDir,
+ mTransportManager.getTransportDirName(t));
+ pw.println(" destination: "
+ + mTransportManager.getTransportCurrentDestinationString(t));
+ pw.println(" intent: "
+ + mTransportManager.getTransportConfigurationIntent(t));
+ for (File f : dir.listFiles()) {
+ pw.println(
+ " " + f.getName() + " - " + f.length() + " state bytes");
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in transport", e);
+ pw.println(" Error: " + e);
+ }
+ }
+ }
+
+ mTransportManager.dumpTransportClients(pw);
+
+ pw.println("Pending init: " + mPendingInits.size());
+ for (String s : mPendingInits) {
+ pw.println(" " + s);
+ }
+
+ pw.print("Ancestral: ");
+ pw.println(Long.toHexString(mAncestralToken));
+ pw.print("Current: ");
+ pw.println(Long.toHexString(mCurrentToken));
+
+ int numPackages = mBackupParticipants.size();
+ pw.println("Participants:");
+ for (int i = 0; i < numPackages; i++) {
+ int uid = mBackupParticipants.keyAt(i);
+ pw.print(" uid: ");
+ pw.println(uid);
+ HashSet<String> participants = mBackupParticipants.valueAt(i);
+ for (String app : participants) {
+ pw.println(" " + app);
+ }
+ }
+
+ pw.println("Ancestral packages: "
+ + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+ if (mAncestralPackages != null) {
+ for (String pkg : mAncestralPackages) {
+ pw.println(" " + pkg);
+ }
+ }
+
+ Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
+ pw.println("Ever backed up: " + processedPackages.size());
+ for (String pkg : processedPackages) {
+ pw.println(" " + pkg);
+ }
+
+ pw.println("Pending key/value backup: " + mPendingBackups.size());
+ for (BackupRequest req : mPendingBackups.values()) {
+ pw.println(" " + req);
+ }
+
+ pw.println("Full backup queue:" + mFullBackupQueue.size());
+ for (FullBackupEntry entry : mFullBackupQueue) {
+ pw.print(" ");
+ pw.print(entry.lastBackup);
+ pw.print(" : ");
+ pw.println(entry.packageName);
+ }
+ }
+ }
+
+
+ public IBackupManager getBackupManagerBinder() {
+ return mBackupManagerBinder;
+ }
+}
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/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index 94365d7..bace1aa 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -1,10 +1,10 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import android.annotation.Nullable;
import android.app.backup.FullBackup;
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index c9f7218..5e92339 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -16,13 +16,13 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -39,8 +39,8 @@
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.FullBackupUtils;
@@ -53,7 +53,7 @@
* and emitting it to the designated OutputStream.
*/
public class FullBackupEngine {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
OutputStream mOutput;
FullBackupPreflight mPreflightHook;
BackupRestoreTask mTimeoutMonitor;
@@ -164,24 +164,21 @@
}
/**
- * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+ * Don't write apks for system-bundled apps that are not upgraded.
*/
private boolean shouldWriteApk(
ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
- boolean isForwardLocked =
- (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
boolean isUpdatedSystemApp =
(applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
return includeApks
&& !isSharedStorage
- && !isForwardLocked
&& (!isSystemApp || isUpdatedSystemApp);
}
}
public FullBackupEngine(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
OutputStream output,
FullBackupPreflight preflightHook,
PackageInfo pkg,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index bc7d9fc..e142537 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -17,8 +17,8 @@
package com.android.server.backup.fullbackup;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -34,7 +34,7 @@
import com.android.internal.backup.IObbBackupService;
import com.android.internal.util.Preconditions;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
import java.io.IOException;
@@ -45,11 +45,11 @@
*/
public class FullBackupObbConnection implements ServiceConnection {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
volatile IObbBackupService mService;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public FullBackupObbConnection(BackupManagerService backupManagerService) {
+ public FullBackupObbConnection(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
mService = null;
mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 44edabc..43a80c4 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -16,13 +16,13 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.backup.IFullBackupRestoreObserver;
import android.content.pm.ApplicationInfo;
@@ -37,7 +37,7 @@
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.KeyValueAdbBackupEngine;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.PasswordUtils;
@@ -66,7 +66,7 @@
*/
public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
FullBackupEngine mBackupEngine;
final AtomicBoolean mLatch;
@@ -86,7 +86,7 @@
String mEncryptPassword;
private final int mCurrentOpToken;
- public PerformAdbBackupTask(BackupManagerService backupManagerService,
+ public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 755095e..5b449c5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,9 +19,9 @@
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;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -45,10 +45,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
@@ -97,7 +97,7 @@
*/
public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
public static PerformFullTransportBackupTask newWithCurrentTransport(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
IFullBackupRestoreObserver observer,
String[] whichPackages,
boolean updateSchedule,
@@ -128,7 +128,7 @@
private static final String TAG = "PFTBT";
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
private final Object mCancelLock = new Object();
ArrayList<PackageInfo> mPackages;
@@ -150,7 +150,7 @@
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
+ public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
TransportClient transportClient,
IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index f66d8cc..ba153bf 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -35,10 +35,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.keyvalue.BackupRequest;
@@ -84,10 +84,10 @@
public static final int MSG_BACKUP_RESTORE_STEP = 20;
public static final int MSG_OP_COMPLETE = 21;
- private final BackupManagerService backupManagerService;
+ private final UserBackupManagerService backupManagerService;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
+ public BackupHandler(UserBackupManagerService backupManagerService, Looper looper) {
super(looper);
this.backupManagerService = backupManagerService;
mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
index b0b8037..396f369 100644
--- a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
@@ -18,13 +18,13 @@
import android.content.pm.IPackageDataObserver;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
public class ClearDataObserver extends IPackageDataObserver.Stub {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
- public ClearDataObserver(BackupManagerService backupManagerService) {
+ public ClearDataObserver(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index d028104..5ffa795 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -22,20 +22,20 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.transport.TransportClient;
import java.io.File;
public class PerformClearTask implements Runnable {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final TransportManager mTransportManager;
private final TransportClient mTransportClient;
private final PackageInfo mPackage;
private final OnTaskFinishedListener mListener;
- PerformClearTask(BackupManagerService backupManagerService,
+ PerformClearTask(UserBackupManagerService backupManagerService,
TransportClient transportClient, PackageInfo packageInfo,
OnTaskFinishedListener listener) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 1ef740d..6b78fbf 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -30,8 +30,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.transport.TransportClient;
import java.io.File;
@@ -49,7 +49,7 @@
* operation was successful then it's {@link BackupTransport#TRANSPORT_OK}.
*/
public class PerformInitializeTask implements Runnable {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final TransportManager mTransportManager;
private final String[] mQueue;
private final File mBaseStateDir;
@@ -57,7 +57,7 @@
@Nullable private IBackupObserver mObserver;
public PerformInitializeTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
String[] transportNames,
@Nullable IBackupObserver observer,
OnTaskFinishedListener listener) {
@@ -72,7 +72,7 @@
@VisibleForTesting
PerformInitializeTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportManager transportManager,
String[] transportNames,
@Nullable IBackupObserver observer,
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
index 69720d4..7e2ac79 100644
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
@@ -23,15 +23,15 @@
import android.os.Handler;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
public class ProvisionedObserver extends ContentObserver {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
public ProvisionedObserver(
- BackupManagerService backupManagerService, Handler handler) {
+ UserBackupManagerService backupManagerService, Handler handler) {
super(handler);
this.backupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 6f574ca..2a5d913 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -18,8 +18,8 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_BACKUP_ACTION;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
import android.app.PendingIntent;
@@ -29,13 +29,13 @@
import android.os.Message;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
public class RunBackupReceiver extends BroadcastReceiver {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
- public RunBackupReceiver(BackupManagerService backupManagerService) {
+ public RunBackupReceiver(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 548c580..38870cb 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -17,8 +17,8 @@
package com.android.server.backup.internal;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_INITIALIZE_ACTION;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_INITIALIZE_ACTION;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -27,12 +27,12 @@
import android.util.ArraySet;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
public class RunInitializeReceiver extends BroadcastReceiver {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
- public RunInitializeReceiver(BackupManagerService backupManagerService) {
+ public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
mBackupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index bb8a1d1..535c7cb 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -30,6 +30,7 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteResult;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
@@ -54,7 +55,7 @@
public class KeyValueBackupReporter {
@VisibleForTesting static final String TAG = "KeyValueBackupTask";
private static final boolean DEBUG = BackupManagerService.DEBUG;
- @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
+ @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG;
static void onNewThread(String threadName) {
if (DEBUG) {
@@ -62,12 +63,12 @@
}
}
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final IBackupObserver mObserver;
@Nullable private IBackupManagerMonitor mMonitor;
KeyValueBackupReporter(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
IBackupObserver observer,
@Nullable IBackupManagerMonitor monitor) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index d6f2a87..f39d795 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -22,9 +22,9 @@
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -35,7 +35,6 @@
import android.app.backup.BackupManager;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupCallback;
-import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.content.pm.ApplicationInfo;
@@ -55,11 +54,11 @@
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
@@ -95,12 +94,12 @@
* <p>A few definitions:
*
* <ul>
- * <li>State directory: {@link BackupManagerService#getBaseStateDir()}/<transport>
+ * <li>State directory: {@link UserBackupManagerService#getBaseStateDir()}/<transport>
* <li>State file: {@link
- * BackupManagerService#getBaseStateDir()}/<transport>/<package><br>
+ * UserBackupManagerService#getBaseStateDir()}/<transport>/<package><br>
* Represents the state of the backup data for a specific package in the current dataset.
- * <li>Stage directory: {@link BackupManagerService#getDataDir()}
- * <li>Stage file: {@link BackupManagerService#getDataDir()}/<package>.data<br>
+ * <li>Stage directory: {@link UserBackupManagerService#getDataDir()}
+ * <li>Stage file: {@link UserBackupManagerService#getDataDir()}/<package>.data<br>
* Contains staged data that the agents wrote via {@link BackupDataOutput}, to be transmitted
* to the transport.
* </ul>
@@ -112,7 +111,7 @@
* of incremental choice. If non-incremental, PM will only be backed-up if specified in the queue,
* and if it's the case it will be re-positioned at the head of the queue.
*
- * <p>Before starting, this task will register itself in {@link BackupManagerService} current
+ * <p>Before starting, this task will register itself in {@link UserBackupManagerService} current
* operations.
*
* <p>In summary, this task will for each package:
@@ -121,7 +120,7 @@
* <li>Bind to its {@link IBackupAgent}.
* <li>Request transport quota and flags.
* <li>Call {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
- * ParcelFileDescriptor, long, int, IBackupManager, int)} via {@link RemoteCall} passing the
+ * ParcelFileDescriptor, long, IBackupCallback, int)} via {@link RemoteCall} passing the
* old state file descriptor (read), the backup data file descriptor (write), the new state
* file descriptor (write), the quota and the transport flags. This will call {@link
* BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} with
@@ -131,7 +130,7 @@
* <ul>
* <li>Agent response.
* <li>Agent time-out (specified via {@link
- * BackupManagerService#getAgentTimeoutParameters()}.
+ * UserBackupManagerService#getAgentTimeoutParameters()}.
* <li>External cancellation or thread interrupt.
* </ul>
* <li>Unbind the agent.
@@ -149,11 +148,11 @@
* <li>Mark data-changed for the remaining packages in the queue (skipped packages).
* <li>Delete the {@link DataChangedJournal} provided. Note that this should not be the current
* journal.
- * <li>Set {@link BackupManagerService} current token as {@link
+ * <li>Set {@link UserBackupManagerService} current token as {@link
* IBackupTransport#getCurrentRestoreSet()}, if applicable.
* <li>Add the transport to the list of transports pending initialization ({@link
- * BackupManagerService#getPendingInits()}) and kick-off initialization if the transport ever
- * returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
+ * UserBackupManagerService#getPendingInits()}) and kick-off initialization if the transport
+ * ever returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
* <li>Unregister the task in current operations.
* <li>Release the wakelock.
* <li>Kick-off {@link PerformFullTransportBackupTask} if a list of full-backup packages was
@@ -174,7 +173,7 @@
private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND;
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
private static final String BLANK_STATE_FILE_NAME = "blank_state";
- private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL;
+ private static final String PM_PACKAGE = UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
@VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
@VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
@@ -182,7 +181,7 @@
* Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
* dedicated thread and kicks off the operation in it.
*
- * @param backupManagerService The {@link BackupManagerService} system service.
+ * @param backupManagerService The {@link UserBackupManagerService} instance.
* @param transportClient The {@link TransportClient} that contains the transport used for the
* operation.
* @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
@@ -201,7 +200,7 @@
* @return The {@link KeyValueBackupTask} that was started.
*/
public static KeyValueBackupTask start(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
String transportDirName,
List<String> queue,
@@ -232,7 +231,7 @@
return task;
}
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final PackageManager mPackageManager;
private final TransportManager mTransportManager;
private final TransportClient mTransportClient;
@@ -289,7 +288,7 @@
@VisibleForTesting
public KeyValueBackupTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
String transportDirName,
List<String> queue,
diff --git a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
index 28d85a6..bfc97ae 100644
--- a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
+++ b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
@@ -20,12 +20,12 @@
import android.app.backup.IBackupManager;
import android.os.RemoteException;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
/**
* An implementation of {@link IBackupCallback} that routes the result to {@link
- * BackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token provided
- * in the constructor.
+ * UserBackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token
+ * provided in the constructor.
*/
public class ServiceBackupCallback extends IBackupCallback.Stub {
private final IBackupManager mBackupManager;
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 140dded..e273b32 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -36,8 +36,8 @@
import android.os.PowerManager;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.RestoreGetSetsParams;
import com.android.server.backup.params.RestoreParams;
@@ -53,14 +53,14 @@
private final TransportManager mTransportManager;
private final String mTransportName;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
@Nullable private final String mPackageName;
public RestoreSet[] mRestoreSets = null;
boolean mEnded = false;
boolean mTimedOut = false;
public ActiveRestoreSession(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
@Nullable String packageName,
String transportName) {
mBackupManagerService = backupManagerService;
@@ -405,10 +405,10 @@
// Posted to the handler to tear down a restore session in a cleanly synchronized way
public class EndRestoreRunnable implements Runnable {
- BackupManagerService mBackupManager;
+ UserBackupManagerService mBackupManager;
ActiveRestoreSession mSession;
- public EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
+ public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) {
mBackupManager = manager;
mSession = session;
}
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index a8c7ce6..e4890e0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -23,8 +23,8 @@
import com.android.internal.util.Preconditions;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -35,12 +35,12 @@
public class AdbRestoreFinishedLatch implements BackupRestoreTask {
private static final String TAG = "AdbRestoreFinishedLatch";
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
final CountDownLatch mLatch;
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
+ public AdbRestoreFinishedLatch(UserBackupManagerService backupManagerService,
int currentOpToken) {
this.backupManagerService = backupManagerService;
mLatch = new CountDownLatch(1);
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
index dc7044e..184a6d0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
@@ -3,7 +3,7 @@
import android.app.IBackupAgent;
import android.os.RemoteException;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
/**
* Runner that can be placed on a separate thread to do in-process invocation of the "restore
@@ -13,10 +13,10 @@
private final IBackupAgent mAgent;
private final int mToken;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
- BackupManagerService backupManagerService) {
+ UserBackupManagerService backupManagerService) {
mAgent = agent;
mToken = token;
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 1084f52..0d26ea5 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -16,13 +16,13 @@
package com.android.server.backup.restore;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import android.app.ApplicationThreadConstants;
@@ -44,10 +44,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BytesReadListener;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -57,7 +57,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -68,7 +67,7 @@
*/
public class FullRestoreEngine extends RestoreEngine {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
// Task in charge of monitoring timeouts
private final BackupRestoreTask mMonitorTask;
@@ -129,7 +128,7 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
final boolean mIsAdbRestore;
- public FullRestoreEngine(BackupManagerService backupManagerService,
+ public FullRestoreEngine(UserBackupManagerService backupManagerService,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 32dbad9..c904256 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,18 +16,15 @@
package com.android.server.backup.restore;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
@@ -36,15 +33,12 @@
import android.content.pm.Signature;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.PackageManagerBackupAgent;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
import com.android.server.backup.utils.PasswordUtils;
@@ -72,7 +66,7 @@
public class PerformAdbRestoreTask implements Runnable {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final ParcelFileDescriptor mInputFile;
private final String mCurrentPassword;
private final String mDecryptPassword;
@@ -106,7 +100,7 @@
// Packages we've already wiped data on when restoring their first file
private final HashSet<String> mClearedPackages = new HashSet<>();
- public PerformAdbRestoreTask(BackupManagerService backupManagerService,
+ public PerformAdbRestoreTask(UserBackupManagerService backupManagerService,
ParcelFileDescriptor fd, String curPassword, String decryptPassword,
IFullBackupRestoreObserver observer, AtomicBoolean latch) {
this.mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 580f70a..f7efad6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -17,12 +17,12 @@
package com.android.server.backup.restore;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -40,8 +40,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -62,8 +62,8 @@
import com.android.server.backup.BackupUtils;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.utils.AppBackupUtils;
@@ -80,7 +80,7 @@
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
private final TransportManager mTransportManager;
// Transport client we're working with to do the restore
private final TransportClient mTransportClient;
@@ -164,7 +164,7 @@
// This task can assume that the wakelock is properly held for it and doesn't have to worry
// about releasing it.
public PerformUnifiedRestoreTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
index 635b6d6..c4aa2d7 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
@@ -21,7 +21,7 @@
import android.os.RemoteException;
import com.android.server.backup.FileMetadata;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import java.io.IOException;
@@ -35,9 +35,9 @@
private final FileMetadata mInfo;
private final ParcelFileDescriptor mSocket;
private final int mToken;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
- RestoreFileRunnable(BackupManagerService backupManagerService, IBackupAgent agent,
+ RestoreFileRunnable(UserBackupManagerService backupManagerService, IBackupAgent agent,
FileMetadata info, ParcelFileDescriptor socket, int token) throws IOException {
mAgent = agent;
mInfo = info;
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index c933833..e465c7e 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -17,8 +17,8 @@
package com.android.server.backup.utils;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.annotation.Nullable;
import android.app.backup.BackupTransport;
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 6dd5284..0f4b681 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -34,14 +34,14 @@
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.backup.BackupAgent;
import android.app.backup.BackupManagerMonitor;
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 62%
rename from services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
rename to services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 9694ab9..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;
@@ -22,6 +22,8 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -32,73 +34,89 @@
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.intelligence.ContentCaptureEvent;
-import android.view.intelligence.IntelligenceManager;
+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.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}.
*/
-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, 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_INTELLIGENCE_SERVICE.equals(si.permission)) {
- Slog.w(TAG, "IntelligenceService 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_INTELLIGENCE_SERVICE);
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
throw new SecurityException("Service does not require permission "
- + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
}
return si;
}
+ @Override // from PerUserSystemService
+ @GuardedBy("mLock")
+ protected boolean updateLocked(boolean disabled) {
+ destroyLocked();
+ return super.updateLocked(disabled);
+ }
+
// TODO(b/111276913): log metrics
@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, IntelligenceManager.STATE_DISABLED);
+ sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
return;
}
final ComponentName serviceComponentName = getServiceComponentName();
@@ -121,13 +139,10 @@
// TODO(b/111276913): check if local ids match and decide what to do if they don't
// TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
// if not, move notifySessionStartedLocked() into session constructor
- sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+ sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
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);
@@ -137,12 +152,13 @@
}
mSessions.put(sessionId, session);
session.notifySessionStartedLocked();
- sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+ sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
}
// 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;
}
@@ -154,16 +170,26 @@
}
return;
}
+ if (events != null && !events.isEmpty()) {
+ // 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 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);
+ }
if (mMaster.verbose) {
- Slog.v(TAG, "finishSession(): " + session);
+ Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
+ + session);
}
session.removeSelfLocked(true);
}
// 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;
@@ -176,7 +202,7 @@
return;
}
if (mMaster.verbose) {
- Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
+ Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
}
session.sendEventsLocked(events);
}
@@ -184,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);
@@ -201,15 +227,26 @@
}
@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();
}
+ @GuardedBy("mLock")
+ private ContentCaptureSession getSession(@NonNull IBinder activityToken) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ if (session.mActivityToken.equals(activityToken)) {
+ return session;
+ }
+ }
+ return null;
+ }
+
/**
* Destroys the service and all state associated with it.
*
@@ -218,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);
@@ -226,6 +268,15 @@
mSessions.clear();
}
+ @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());
+ }
+ }
+
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
@@ -244,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/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
similarity index 70%
rename from services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
rename to services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
index 57e954f..2302b7d 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
@@ -13,50 +13,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.intelligence;
+package com.android.server.contentcapture;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
-import android.service.intelligence.IntelligenceService;
-import android.service.intelligence.InteractionContext;
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.SnapshotData;
+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.intelligence.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
-import com.android.server.AbstractRemoteService;
-import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
+import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
+import com.android.server.infra.AbstractRemoteService;
import java.io.PrintWriter;
import java.util.List;
-final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks {
+final class ContentCaptureSession implements ContentCaptureServiceCallbacks {
private static final String TAG = "ContentCaptureSession";
private final Object mLock;
- private final IBinder mActivityToken;
-
- private final IntelligencePerUserService mService;
- private final RemoteIntelligenceService mRemoteService;
+ final IBinder mActivityToken;
+ private final ContentCapturePerUserService mService;
+ private final RemoteContentCaptureService mRemoteService;
private final InteractionContext mInterationContext;
- private final InteractionSessionId mId;
+ private final String mId;
ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
- @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
+ @NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
@NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
- int taskId, int displayId, @NonNull InteractionSessionId sessionId, int flags,
+ 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 RemoteIntelligenceService(context,
- IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
+ mRemoteService = new RemoteContentCaptureService(context,
+ ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
bindInstantServiceAllowed, verbose);
mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
}
@@ -69,7 +68,7 @@
}
/**
- * Notifies the {@link IntelligenceService} that the service started.
+ * Notifies the {@link ContentCaptureService} that the service started.
*/
@GuardedBy("mLock")
public void notifySessionStartedLocked() {
@@ -77,14 +76,14 @@
}
/**
- * Notifies the {@link IntelligenceService} of a batch of events.
+ * Notifies the {@link ContentCaptureService} of a batch of events.
*/
public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
mRemoteService.onContentCaptureEventsRequest(mId, events);
}
/**
- * Notifies the {@link IntelligenceService} of a snapshot of an activity.
+ * Notifies the {@link ContentCaptureService} of a snapshot of an activity.
*/
@GuardedBy("mLock")
public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
@@ -95,7 +94,7 @@
* Cleans up the session and removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
- * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+ * ContentCaptureService#onDestroyInteractionSession(InteractionSessionId)}
* request.
*/
@GuardedBy("mLock")
@@ -111,7 +110,7 @@
* Cleans up the session, but not removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
- * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+ * ContentCaptureService#onDestroyInteractionSession(InteractionSessionId)}
* request.
*/
@GuardedBy("mLock")
@@ -126,7 +125,7 @@
}
@Override // from RemoteScreenObservationServiceCallbacks
- public void onServiceDied(AbstractRemoteService service) {
+ public void onServiceDied(AbstractRemoteService<?> service) {
// TODO(b/111276913): implement (remove session from PerUserSession?)
if (mService.isDebug()) {
Slog.d(TAG, "onServiceDied() for " + mId);
@@ -149,13 +148,18 @@
@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("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.getValue() + ", act=" + mActivityToken + "]";
+ return "ContentCaptureSession[id=" + mId + ", act=" + mActivityToken + "]";
}
}
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
similarity index 64%
rename from services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
rename to services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index a27c1cf..6a111f2 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.intelligence;
+package com.android.server.contentcapture;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -22,40 +22,42 @@
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
-import android.service.intelligence.IIntelligenceService;
-import android.service.intelligence.InteractionContext;
-import android.service.intelligence.InteractionSessionId;
-import android.service.intelligence.SnapshotData;
+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.util.Slog;
-import android.view.intelligence.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureEvent;
-import com.android.server.AbstractRemoteService;
+import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
import java.util.List;
-final class RemoteIntelligenceService extends AbstractRemoteService {
+final class RemoteContentCaptureService
+ extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService> {
- private static final String TAG = "RemoteIntelligenceService";
+ private static final String TAG = RemoteContentCaptureService.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.MINUTE_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
- private final RemoteIntelligenceServiceCallbacks mCallbacks;
- private IIntelligenceService mService;
+ private final ContentCaptureServiceCallbacks mCallbacks;
+ private IContentCaptureService mService;
- RemoteIntelligenceService(Context context, String serviceInterface,
+ RemoteContentCaptureService(Context context, String serviceInterface,
ComponentName componentName, int userId,
- RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
+ ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callbacks,
- bindInstantServiceAllowed, verbose);
+ bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
mCallbacks = callbacks;
}
@Override // from RemoteService
protected IInterface getServiceInterface(@NonNull IBinder service) {
- mService = IIntelligenceService.Stub.asInterface(service);
+ mService = IContentCaptureService.Stub.asInterface(service);
return mService;
}
@@ -74,11 +76,11 @@
/**
* Called by {@link ContentCaptureSession} to generate a call to the
- * {@link RemoteIntelligenceService} to indicate the session was created (when {@code context}
+ * {@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 InteractionSessionId sessionId) {
+ @NonNull String sessionId) {
cancelScheduledUnbind();
scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
}
@@ -86,7 +88,7 @@
/**
* Called by {@link ContentCaptureSession} to send a batch of events to the service.
*/
- public void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+ public void onContentCaptureEventsRequest(@NonNull String sessionId,
@NonNull List<ContentCaptureEvent> events) {
cancelScheduledUnbind();
scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
@@ -95,25 +97,24 @@
/**
* Called by {@link ContentCaptureSession} to send snapshot data to the service.
*/
- public void onActivitySnapshotRequest(@NonNull InteractionSessionId sessionId,
+ public void onActivitySnapshotRequest(@NonNull String sessionId,
@NonNull SnapshotData snapshotData) {
cancelScheduledUnbind();
scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
}
-
private abstract static class MyPendingRequest
- extends PendingRequest<RemoteIntelligenceService> {
- protected final InteractionSessionId mSessionId;
+ extends PendingRequest<RemoteContentCaptureService> {
+ protected final String mSessionId;
- private MyPendingRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId) {
+ private MyPendingRequest(@NonNull RemoteContentCaptureService service,
+ @NonNull String sessionId) {
super(service);
mSessionId = sessionId;
}
@Override // from PendingRequest
- protected final void onTimeout(RemoteIntelligenceService remoteService) {
+ protected final void onTimeout(RemoteContentCaptureService remoteService) {
Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
+ mSessionId);
remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
@@ -121,11 +122,12 @@
@Override // from PendingRequest
public final void run() {
- final RemoteIntelligenceService remoteService = getService();
+ final RemoteContentCaptureService remoteService = getService();
if (remoteService != null) {
try {
- myRun(remoteService);
// 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 "
@@ -135,7 +137,7 @@
}
}
- protected abstract void myRun(@NonNull RemoteIntelligenceService service)
+ protected abstract void myRun(@NonNull RemoteContentCaptureService service)
throws RemoteException;
}
@@ -144,14 +146,15 @@
private final InteractionContext mContext;
- protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service,
- @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) {
+ protected PendingSessionLifecycleRequest(@NonNull RemoteContentCaptureService service,
+ @Nullable InteractionContext context, @NonNull String sessionId) {
super(service, sessionId);
mContext = context;
}
@Override // from MyPendingRequest
- public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+ public void myRun(@NonNull RemoteContentCaptureService remoteService)
+ throws RemoteException {
remoteService.mService.onSessionLifecycle(mContext, mSessionId);
}
}
@@ -160,16 +163,17 @@
private final List<ContentCaptureEvent> mEvents;
- protected PendingOnContentCaptureEventsRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId,
- @NonNull List<ContentCaptureEvent> events) {
+ protected PendingOnContentCaptureEventsRequest(@NonNull RemoteContentCaptureService service,
+ @NonNull String sessionId, @NonNull List<ContentCaptureEvent> events) {
super(service, sessionId);
mEvents = events;
}
@Override // from MyPendingRequest
- public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
- remoteService.mService.onContentCaptureEvents(mSessionId, mEvents);
+ public void myRun(@NonNull RemoteContentCaptureService remoteService)
+ throws RemoteException {
+ remoteService.mService.onContentCaptureEventsRequest(mSessionId,
+ new ContentCaptureEventsRequest(mEvents));
}
}
@@ -177,21 +181,20 @@
private final SnapshotData mSnapshotData;
- protected PendingOnActivitySnapshotRequest(@NonNull RemoteIntelligenceService service,
- @NonNull InteractionSessionId sessionId,
- @NonNull SnapshotData snapshotData) {
+ protected PendingOnActivitySnapshotRequest(@NonNull RemoteContentCaptureService service,
+ @NonNull String sessionId, @NonNull SnapshotData snapshotData) {
super(service, sessionId);
mSnapshotData = snapshotData;
}
@Override // from MyPendingRequest
- protected void myRun(@NonNull RemoteIntelligenceService remoteService)
+ protected void myRun(@NonNull RemoteContentCaptureService remoteService)
throws RemoteException {
remoteService.mService.onActivitySnapshot(mSessionId, mSnapshotData);
}
}
- public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
+ public interface ContentCaptureServiceCallbacks extends VultureCallback {
// To keep it simple, we use the same callback for all failures / timeouts.
void onFailureOrTimeout(boolean timedOut);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6174300..70b8339 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -15,14 +15,14 @@
"java/**/*.java",
":dumpstate_aidl",
":idmap2_aidl",
- ":netd_aidl",
- ":netd_metrics_aidl",
":installd_aidl",
":storaged_aidl",
":vold_aidl",
":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
+ ":netd_aidl",
+ ":netd_metrics_aidl",
],
libs: [
@@ -32,6 +32,10 @@
"android.hardware.tv.cec-V1.0-java",
],
+ required: [
+ "gps_debug.conf",
+ ],
+
static_libs: [
"time_zone_distro",
"time_zone_distro_installer",
@@ -69,3 +73,9 @@
name: "services.core",
static_libs: ["services.core.priorityboosted"],
}
+
+
+prebuilt_etc {
+ name: "gps_debug.conf",
+ src: "java/com/android/server/location/gps_debug.conf",
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 356a4da..8d912fa 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -692,7 +692,7 @@
}
});
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index dd96075..11a2fc9 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -16,23 +16,33 @@
package com.android.server;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
import android.app.AppGlobals;
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.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.ThreadLocalWorkSource;
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.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.CallSession;
import com.android.internal.os.CachedDeviceState;
import java.io.FileDescriptor;
@@ -49,6 +59,106 @@
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 WorkSourceProvider {
+ private ArraySet<Integer> mAppIdWhitelist;
+
+ WorkSourceProvider() {
+ 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, Map<Integer, String> appIdToPackageName) {
+ pw.println("AppIds of apps that can set the work source:");
+ final ArraySet<Integer> whitelist = mAppIdWhitelist;
+ for (Integer appId : whitelist) {
+ pw.println("\t- " + appIdToPackageName.getOrDefault(appId, String.valueOf(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(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;
+ }
+ }
+
+ /** Observer for all system server incoming binder transactions. */
+ @VisibleForTesting
+ static class BinderCallsObserver implements BinderInternal.Observer {
+ private final BinderInternal.Observer mBinderCallsStats;
+ private final WorkSourceProvider mWorkSourceProvider;
+
+ BinderCallsObserver(BinderInternal.Observer callsStats,
+ WorkSourceProvider workSourceProvider) {
+ mBinderCallsStats = callsStats;
+ mWorkSourceProvider = workSourceProvider;
+ }
+
+ @Override
+ public CallSession callStarted(Binder binder, int code) {
+ // We depend on the code in Binder#execTransact to reset the state of
+ // ThreadLocalWorkSource
+ setThreadLocalWorkSourceUid(mWorkSourceProvider.resolveWorkSourceUid());
+ return mBinderCallsStats.callStarted(binder, code);
+ }
+
+ @Override
+ public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+ mBinderCallsStats.callEnded(s, parcelRequestSize, parcelReplySize);
+ }
+
+ @Override
+ public void callThrewException(CallSession s, Exception exception) {
+ mBinderCallsStats.callThrewException(s, exception);
+ }
+
+ protected void setThreadLocalWorkSourceUid(int uid) {
+ ThreadLocalWorkSource.setUid(uid);
+ }
+ }
/** Listens for flag changes. */
private static class SettingsObserver extends ContentObserver {
@@ -56,19 +166,23 @@
private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
private boolean mEnabled;
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final BinderCallsStats mBinderCallsStats;
+ private final BinderCallsObserver mBinderCallsObserver;
- public SettingsObserver(Context context, BinderCallsStats binderCallsStats) {
+ SettingsObserver(Context context, BinderCallsStats binderCallsStats,
+ BinderCallsObserver observer) {
super(BackgroundThread.getHandler());
mContext = context;
context.getContentResolver().registerContentObserver(mUri, false, this,
UserHandle.USER_SYSTEM);
mBinderCallsStats = binderCallsStats;
+ mBinderCallsObserver = observer;
// Always kick once to ensure that we match current state
onChange();
}
@@ -97,13 +211,16 @@
mBinderCallsStats.setSamplingInterval(mParser.getInt(
SETTINGS_SAMPLING_INTERVAL_KEY,
BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+ mBinderCallsStats.setMaxBinderCallStats(mParser.getInt(
+ SETTINGS_MAX_CALL_STATS_KEY,
+ BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT));
final boolean enabled =
mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
if (mEnabled != enabled) {
if (enabled) {
- Binder.setObserver(mBinderCallsStats);
+ Binder.setObserver(mBinderCallsObserver);
Binder.setProxyTransactListener(
new Binder.PropagateWorkSourceTransactListener());
} else {
@@ -112,6 +229,7 @@
}
mEnabled = enabled;
mBinderCallsStats.reset();
+ mBinderCallsStats.setAddDebugEntries(enabled);
}
}
}
@@ -150,6 +268,7 @@
public static class LifeCycle extends SystemService {
private BinderCallsStatsService mService;
private BinderCallsStats mBinderCallsStats;
+ private WorkSourceProvider mWorkSourceProvider;
public LifeCycle(Context context) {
super(context);
@@ -158,7 +277,11 @@
@Override
public void onStart() {
mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
- mService = new BinderCallsStatsService(mBinderCallsStats);
+ mWorkSourceProvider = new WorkSourceProvider();
+ BinderCallsObserver binderCallsObserver =
+ new BinderCallsObserver(mBinderCallsStats, mWorkSourceProvider);
+ mService = new BinderCallsStatsService(
+ mBinderCallsStats, binderCallsObserver, mWorkSourceProvider);
publishLocalService(Internal.class, new Internal(mBinderCallsStats));
publishBinderService("binder_calls_stats", mService);
boolean detailedTrackingEnabled = SystemProperties.getBoolean(
@@ -177,21 +300,29 @@
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 BinderCallsObserver mBinderCallsObserver;
+ private final WorkSourceProvider mWorkSourceProvider;
- BinderCallsStatsService(BinderCallsStats binderCallsStats) {
+ BinderCallsStatsService(BinderCallsStats binderCallsStats, BinderCallsObserver observer,
+ WorkSourceProvider workSourceProvider) {
mBinderCallsStats = binderCallsStats;
+ mBinderCallsObserver = observer;
+ mWorkSourceProvider = workSourceProvider;
}
public void systemReady(Context context) {
- mSettingsObserver = new SettingsObserver(context, mBinderCallsStats);
+ mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mBinderCallsObserver);
}
public void reset() {
@@ -211,7 +342,7 @@
pw.println("binder_calls_stats reset.");
return;
} else if ("--enable".equals(arg)) {
- Binder.setObserver(mBinderCallsStats);
+ Binder.setObserver(mBinderCallsObserver);
return;
} else if ("--disable".equals(arg)) {
Binder.setObserver(null);
@@ -229,6 +360,9 @@
mBinderCallsStats.setDetailedTracking(false);
pw.println("Detailed tracking disabled");
return;
+ } else if ("--dump-worksource-provider".equals(arg)) {
+ mWorkSourceProvider.dump(pw, getAppIdToPackagesMap());
+ return;
} else if ("-h".equals(arg)) {
pw.println("binder_calls_stats commands:");
pw.println(" --reset: Reset stats");
@@ -267,5 +401,4 @@
}
return map;
}
-
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74c8023..14503f9 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;
@@ -894,10 +895,18 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
+ // Listen to package add and removal events for all users.
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiverAsUser(
+ mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
try {
mNMS.registerObserver(mTethering);
mNMS.registerObserver(mDataActivityObserver);
@@ -1659,6 +1668,24 @@
loge("Error parsing ip address in validation event");
}
}
+
+ @Override
+ public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+ String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+ // Netd event only allow registrants from system. Each NetworkMonitor thread is under
+ // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
+ // event callback for certain nai. e.g. cellular. Register here to pass to
+ // NetworkMonitor instead.
+ // TODO: Move the Dns Event to NetworkMonitor. Use Binder.clearCallingIdentity() in
+ // registerNetworkAgent to have NetworkMonitor created with system process as design
+ // expectation. Also, NetdEventListenerService only allow one callback from each
+ // caller type. Need to re-factor NetdEventListenerService to allow multiple
+ // NetworkMonitor registrants.
+ if (nai != null && nai.satisfies(mDefaultRequest)) {
+ nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+ }
+ }
};
@VisibleForTesting
@@ -2064,7 +2091,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.
@@ -2073,12 +2101,7 @@
return new MockableSystemProperties();
}
- private void updateTcpBufferSizes(NetworkAgentInfo nai) {
- if (isDefaultNetwork(nai) == false) {
- return;
- }
-
- String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes();
+ private void updateTcpBufferSizes(String tcpBufferSizes) {
String[] values = null;
if (tcpBufferSizes != null) {
values = tcpBufferSizes.split(",");
@@ -2095,15 +2118,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);
}
@@ -4155,6 +4174,7 @@
}
private void onUserAdded(int userId) {
+ mPermissionMonitor.onUserAdded(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
@@ -4165,6 +4185,7 @@
}
private void onUserRemoved(int userId) {
+ mPermissionMonitor.onUserRemoved(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
@@ -4174,6 +4195,22 @@
}
}
+ private void onPackageAdded(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+ return;
+ }
+ mPermissionMonitor.onPackageAdded(packageName, uid);
+ }
+
+ private void onPackageRemoved(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+ return;
+ }
+ mPermissionMonitor.onPackageRemoved(uid);
+ }
+
private void onUserUnlocked(int userId) {
synchronized (mVpns) {
// User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -4185,11 +4222,15 @@
}
}
- private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
if (userId == UserHandle.USER_NULL) return;
if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -4202,6 +4243,10 @@
onUserRemoved(userId);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
onUserUnlocked(userId);
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, uid);
}
}
};
@@ -4728,8 +4773,8 @@
updateUids(nai, null, nai.networkCapabilities);
}
- private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
- LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
+ private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+ LinkProperties oldLp) {
int netId = networkAgent.network.netId;
// The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4744,7 +4789,9 @@
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
// }
- updateTcpBufferSizes(networkAgent);
+ if (isDefaultNetwork(networkAgent)) {
+ updateTcpBufferSizes(newLp.getTcpBufferSizes());
+ }
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);
@@ -4754,8 +4801,6 @@
// updateDnses will fetch the private DNS configuration from DnsManager.
mDnsManager.updatePrivateDnsStatus(netId, newLp);
- // Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNMS);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4766,8 +4811,12 @@
synchronized (networkAgent) {
networkAgent.linkProperties = newLp;
}
+ // Start or stop clat accordingly to network state.
+ networkAgent.updateClat(mNMS);
notifyIfacesChangedForNetworkStats();
- notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+ if (networkAgent.everConnected) {
+ notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+ }
}
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
@@ -5072,13 +5121,7 @@
"; created=" + nai.created +
"; everConnected=" + nai.everConnected);
}
- LinkProperties oldLp = nai.linkProperties;
- synchronized (nai) {
- nai.linkProperties = newLp;
- }
- if (nai.everConnected) {
- updateLinkProperties(nai, oldLp);
- }
+ updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
@@ -5239,7 +5282,7 @@
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
- updateTcpBufferSizes(newNetwork);
+ updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
}
@@ -5654,7 +5697,8 @@
}
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
- updateLinkProperties(networkAgent, null);
+ updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
+ null);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
scheduleUnvalidatedPrompt(networkAgent);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 8c25917..126bf65 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -24,6 +24,7 @@
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
@@ -62,6 +63,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import libcore.io.IoUtils;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -73,8 +76,6 @@
import java.util.ArrayList;
import java.util.List;
-import libcore.io.IoUtils;
-
/**
* A service to manage multiple clients that want to access the IpSec API. The service is
* responsible for maintaining a list of clients and managing the resources (and related quotas)
@@ -621,7 +622,8 @@
mConfig.getDestinationAddress(),
spi,
mConfig.getMarkValue(),
- mConfig.getMarkMask());
+ mConfig.getMarkMask(),
+ mConfig.getXfrmInterfaceId());
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
}
@@ -683,7 +685,8 @@
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+ uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+ 0 /* mask */, 0 /* if_id */);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -795,6 +798,8 @@
private final int mIkey;
private final int mOkey;
+ private final int mIfId;
+
TunnelInterfaceRecord(
int resourceId,
String interfaceName,
@@ -802,7 +807,8 @@
String localAddr,
String remoteAddr,
int ikey,
- int okey) {
+ int okey,
+ int intfId) {
super(resourceId);
mInterfaceName = interfaceName;
@@ -811,6 +817,7 @@
mRemoteAddress = remoteAddr;
mIkey = ikey;
mOkey = okey;
+ mIfId = intfId;
}
/** always guarded by IpSecService#this */
@@ -821,7 +828,7 @@
// Delete global policies
try {
final INetd netd = mSrvConfig.getNetdInstance();
- netd.removeVirtualTunnelInterface(mInterfaceName);
+ netd.ipSecRemoveTunnelInterface(mInterfaceName);
for (int selAddrFamily : ADDRESS_FAMILIES) {
netd.ipSecDeleteSecurityPolicy(
@@ -829,13 +836,15 @@
selAddrFamily,
IpSecManager.DIRECTION_OUT,
mOkey,
- 0xffffffff);
+ 0xffffffff,
+ mIfId);
netd.ipSecDeleteSecurityPolicy(
uid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
mIkey,
- 0xffffffff);
+ 0xffffffff,
+ mIfId);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(
@@ -877,6 +886,10 @@
return mOkey;
}
+ public int getIfId() {
+ return mIfId;
+ }
+
@Override
protected ResourceTracker getResourceTracker() {
return getUserRecord().mTunnelQuotaTracker;
@@ -1223,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);
@@ -1286,7 +1300,7 @@
// Add inbound/outbound global policies
// (use reqid = 0)
final INetd netd = mSrvConfig.getNetdInstance();
- netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
+ netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
for (int selAddrFamily : ADDRESS_FAMILIES) {
// Always send down correct local/remote addresses for template.
@@ -1298,7 +1312,8 @@
remoteAddr,
0,
okey,
- 0xffffffff);
+ 0xffffffff,
+ resourceId);
netd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
@@ -1307,7 +1322,8 @@
localAddr,
0,
ikey,
- 0xffffffff);
+ 0xffffffff,
+ resourceId);
}
userRecord.mTunnelInterfaceRecords.put(
@@ -1320,7 +1336,8 @@
localAddr,
remoteAddr,
ikey,
- okey),
+ okey,
+ resourceId),
binder));
return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
} catch (RemoteException e) {
@@ -1523,6 +1540,9 @@
throw new IllegalArgumentException(
"Invalid IpSecTransform.mode: " + config.getMode());
}
+
+ config.setMarkValue(0);
+ config.setMarkMask(0);
}
private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
@@ -1584,7 +1604,8 @@
(authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
encapType,
encapLocalPort,
- encapRemotePort);
+ encapRemotePort,
+ c.getXfrmInterfaceId());
}
/**
@@ -1676,7 +1697,7 @@
mSrvConfig
.getNetdInstance()
.ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
+ socket,
callingUid,
direction,
c.getSourceAddress(),
@@ -1695,7 +1716,7 @@
throws RemoteException {
mSrvConfig
.getNetdInstance()
- .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
+ .ipSecRemoveTransportModeTransform(socket);
}
/**
@@ -1740,27 +1761,48 @@
: tunnelInterfaceInfo.getIkey();
try {
- c.setMarkValue(mark);
- c.setMarkMask(0xffffffff);
+ // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
+ // SPI matching as part of the template resolution.
+ int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
+ c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
+
+ // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
+ // (and backporting) would allow us to narrow the mark space, and ensure that the SA
+ // and SPs have matching marks (as VTI are meant to be built).
+ // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
+ // config matches the actual allocated resources in the kernel.
+ // All SAs will have zero marks (from creation time), and any policy that matches the
+ // same src/dst could match these SAs. Non-IpSecService governed processes that
+ // establish floating policies with the same src/dst may result in undefined
+ // behavior. This is generally limited to vendor code due to the permissions
+ // (CAP_NET_ADMIN) required.
+ //
+ // c.setMarkValue(mark);
+ // c.setMarkMask(0xffffffff);
if (direction == IpSecManager.DIRECTION_OUT) {
// Set output mark via underlying network (output only)
c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
- // If outbound, also add SPI to the policy.
- for (int selAddrFamily : ADDRESS_FAMILIES) {
- mSrvConfig
- .getNetdInstance()
- .ipSecUpdateSecurityPolicy(
- callingUid,
- selAddrFamily,
- direction,
- tunnelInterfaceInfo.getLocalAddress(),
- tunnelInterfaceInfo.getRemoteAddress(),
- transformInfo.getSpiRecord().getSpi(),
- mark,
- 0xffffffff);
- }
+ // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
+ // but want to guarantee outbound packets are sent over the new SA.
+ spi = transformInfo.getSpiRecord().getSpi();
+ }
+
+ // Always update the policy with the relevant XFRM_IF_ID
+ for (int selAddrFamily : ADDRESS_FAMILIES) {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecUpdateSecurityPolicy(
+ callingUid,
+ selAddrFamily,
+ direction,
+ transformInfo.getConfig().getSourceAddress(),
+ transformInfo.getConfig().getDestinationAddress(),
+ spi, // If outbound, also add SPI to the policy.
+ mark, // Must always set policy mark; ikey/okey for VTIs
+ 0xffffffff,
+ c.getXfrmInterfaceId());
}
// Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 046442a..cc7bf33 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,8 +17,8 @@
package com.android.server;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -84,7 +84,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
@@ -113,7 +112,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.Set;
/**
@@ -2990,7 +2988,7 @@
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> deadUpdateRecords = null;
- // Broadcast location or status to all listeners
+ // Broadcast location to all listeners
for (UpdateRecord r : records) {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
@@ -3049,14 +3047,19 @@
}
}
- long prevStatusUpdateTime = r.mLastStatusBroadcast;
- if ((newStatusUpdateTime > prevStatusUpdateTime) &&
- (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+ // TODO: location provider status callbacks have been disabled and deprecated, and are
+ // guarded behind this setting now. should be removed completely post-Q
+ if (Settings.Global.getInt(mContext.getContentResolver(),
+ LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
+ long prevStatusUpdateTime = r.mLastStatusBroadcast;
+ if ((newStatusUpdateTime > prevStatusUpdateTime)
+ && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
- r.mLastStatusBroadcast = newStatusUpdateTime;
- if (!receiver.callStatusChangedLocked(provider, status, extras)) {
- receiverDead = true;
- Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+ r.mLastStatusBroadcast = newStatusUpdateTime;
+ if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+ receiverDead = true;
+ Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+ }
}
}
@@ -3276,7 +3279,6 @@
// we don't leave anything dangling.
clearTestProviderEnabled(provider, opPackageName);
clearTestProviderLocation(provider, opPackageName);
- clearTestProviderStatus(provider, opPackageName);
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
@@ -3408,63 +3410,6 @@
}
}
- @Override
- public void clearTestProviderStatus(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- mockProvider.clearStatus();
- }
- }
-
- @Override
- public PendingIntent createManageLocationPermissionIntent(String packageName,
- String permission) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkArgument(permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)
- || permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)
- || permission.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION));
-
- int callingUid = Binder.getCallingUid();
- long token = Binder.clearCallingIdentity();
- try {
- String locProvider = getNetworkProviderPackage();
- if (locProvider == null) {
- return null;
- }
-
- PackageInfo locProviderInfo;
- try {
- locProviderInfo = mContext.getPackageManager().getPackageInfo(
- locProvider, PackageManager.MATCH_DIRECT_BOOT_AUTO);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Could not resolve " + locProvider, e);
- return null;
- }
-
- if (locProviderInfo.applicationInfo.uid != callingUid) {
- throw new SecurityException("Only " + locProvider + " can call this API");
- }
-
- Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_PERMISSION_NAME, permission);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- return PendingIntent.getActivity(mContext,
- Objects.hash(packageName, permission), intent,
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
private void log(String log) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, log);
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 2dee3a0..fa3baba 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -94,6 +94,8 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
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)
@@ -141,6 +143,7 @@
if (mEnabled != enabled) {
mEnabled = enabled;
mStats.reset();
+ mStats.setAddDebugEntries(enabled);
Looper.setObserver(enabled ? mStats : null);
}
}
diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java
new file mode 100644
index 0000000..bb39ccc
--- /dev/null
+++ b/services/core/java/com/android/server/RuntimeService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.Binder;
+import android.service.runtime.DebugEntryProto;
+import android.service.runtime.RuntimeServiceInfoProto;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import libcore.timezone.TimeZoneDataFiles;
+import libcore.util.CoreLibraryDebug;
+import libcore.util.DebugInfo;
+
+import com.android.internal.util.DumpUtils;
+import com.android.timezone.distro.DistroException;
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.FileUtils;
+import com.android.timezone.distro.TimeZoneDistro;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * This service exists only as a "dumpsys" target which reports information about the status of the
+ * runtime and related libraries.
+ */
+public class RuntimeService extends Binder {
+
+ private static final String TAG = "RuntimeService";
+
+ private final Context mContext;
+
+ public RuntimeService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+ return;
+ }
+
+ boolean protoFormat = hasOption(args, "--proto");
+ ProtoOutputStream proto = null;
+
+ DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo();
+ addTimeZoneApkDebugInfo(coreLibraryDebugInfo);
+
+ if (protoFormat) {
+ proto = new ProtoOutputStream(fd);
+ reportTimeZoneInfoProto(coreLibraryDebugInfo, proto);
+ } else {
+ reportTimeZoneInfo(coreLibraryDebugInfo, pw);
+ }
+
+ if (protoFormat) {
+ proto.flush();
+ }
+ }
+
+ /** Returns {@code true} if {@code args} contains {@code arg}. */
+ private static boolean hasOption(String[] args, String arg) {
+ for (String opt : args) {
+ if (arg.equals(opt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add information to {@link DebugInfo} about the time zone data supplied by the
+ * "Time zone updates via APK" feature.
+ */
+ private static void addTimeZoneApkDebugInfo(DebugInfo coreLibraryDebugInfo) {
+ // 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.source.data_";
+ String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
+ TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
+ addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
+ }
+
+ /**
+ * Prints {@code coreLibraryDebugInfo} to {@code pw}.
+ *
+ * <p>If you change this method, make sure to modify
+ * {@link #reportTimeZoneInfoProto(DebugInfo, ProtoOutputStream)} as well.
+ */
+ private static void reportTimeZoneInfo(DebugInfo coreLibraryDebugInfo,
+ PrintWriter pw) {
+ pw.println("Core Library Debug Info: ");
+ for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
+ pw.print(debugEntry.getKey());
+ pw.print(": \"");
+ pw.print(debugEntry.getStringValue());
+ pw.println("\"");
+ }
+ }
+
+ /**
+ * Adds {@code coreLibraryDebugInfo} to {@code protoStream}.
+ *
+ * <p>If you change this method, make sure to modify
+ * {@link #reportTimeZoneInfo(DebugInfo, PrintWriter)}.
+ */
+ private static void reportTimeZoneInfoProto(
+ DebugInfo coreLibraryDebugInfo, ProtoOutputStream protoStream) {
+ for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
+ long entryToken = protoStream.start(RuntimeServiceInfoProto.DEBUG_ENTRY);
+ protoStream.write(DebugEntryProto.KEY, debugEntry.getKey());
+ protoStream.write(DebugEntryProto.STRING_VALUE, debugEntry.getStringValue());
+ protoStream.end(entryToken);
+ }
+ }
+
+ /**
+ * Adds version information to {@code debugInfo} from the distro_version file that may exist
+ * at {@code distroVersionFileName}. If the file does not exist or cannot be read this is
+ * reported as debug information too.
+ */
+ private static void addDistroVersionDebugInfo(String distroVersionFileName,
+ String debugKeyPrefix, DebugInfo debugInfo) {
+ File file = new File(distroVersionFileName);
+ String statusKey = debugKeyPrefix + "status";
+ if (file.exists()) {
+ try {
+ byte[] versionBytes =
+ FileUtils.readBytes(file, DistroVersion.DISTRO_VERSION_FILE_LENGTH);
+ DistroVersion distroVersion = DistroVersion.fromBytes(versionBytes);
+ String formatVersionString = distroVersion.formatMajorVersion + "."
+ + distroVersion.formatMinorVersion;
+ debugInfo.addStringEntry(statusKey, "OK")
+ .addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString)
+ .addStringEntry(debugKeyPrefix + "rulesVersion",
+ distroVersion.rulesVersion)
+ .addStringEntry(debugKeyPrefix + "revision",
+ distroVersion.revision);
+ } catch (IOException | DistroException e) {
+ debugInfo.addStringEntry(statusKey, "ERROR");
+ debugInfo.addStringEntry(debugKeyPrefix + "exception_class",
+ e.getClass().getName());
+ debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage());
+ logMessage("Error reading " + file, e);
+ }
+ } else {
+ debugInfo.addStringEntry(statusKey, "NOT_FOUND");
+ }
+ }
+
+ private static void logMessage(String msg, Throwable t) {
+ Slog.v(TAG, msg, t);
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 923ac00..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;
@@ -183,8 +186,7 @@
private static final String ZRAM_ENABLED_PROPERTY =
"persist.sys.zram_enabled";
- private static final boolean ENABLE_ISOLATED_STORAGE = SystemProperties
- .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+ private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -1016,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();
}
}
@@ -2524,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,
@@ -2686,24 +2701,35 @@
class AppFuseMountScope extends AppFuseBridge.MountScope {
boolean opened = false;
- public AppFuseMountScope(int uid, int pid, int mountId) {
- super(uid, pid, mountId);
+ public AppFuseMountScope(int uid, int mountId) {
+ super(uid, mountId);
}
@Override
public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
try {
return new ParcelFileDescriptor(
- mVold.mountAppFuse(uid, Process.myPid(), mountId));
+ mVold.mountAppFuse(uid, mountId));
} catch (Exception e) {
throw new NativeDaemonConnectorException("Failed to mount", e);
}
}
@Override
+ public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
+ throws NativeDaemonConnectorException {
+ try {
+ return new ParcelFileDescriptor(
+ mVold.openAppFuseFile(uid, mountId, fileId, flags));
+ } catch (Exception e) {
+ throw new NativeDaemonConnectorException("Failed to open", e);
+ }
+ }
+
+ @Override
public void close() throws Exception {
if (opened) {
- mVold.unmountAppFuse(uid, Process.myPid(), mountId);
+ mVold.unmountAppFuse(uid, mountId);
opened = false;
}
}
@@ -2713,7 +2739,6 @@
public @Nullable AppFuseMount mountProxyFileDescriptorBridge() {
Slog.v(TAG, "mountProxyFileDescriptorBridge");
final int uid = Binder.getCallingUid();
- final int pid = Binder.getCallingPid();
while (true) {
synchronized (mAppFuseLock) {
@@ -2727,7 +2752,7 @@
final int name = mNextAppFuseName++;
try {
return new AppFuseMount(
- name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name)));
+ name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, name)));
} catch (FuseUnavailableMountException e) {
if (newlyCreated) {
// If newly created bridge fails, it's a real error.
@@ -2748,14 +2773,21 @@
public @Nullable ParcelFileDescriptor openProxyFileDescriptor(
int mountId, int fileId, int mode) {
Slog.v(TAG, "mountProxyFileDescriptor");
- final int pid = Binder.getCallingPid();
+
+ // 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) {
Slog.e(TAG, "FuseBridge has not been created");
return null;
}
- return mAppFuseBridge.openFile(pid, mountId, fileId, mode);
+ return mAppFuseBridge.openFile(mountId, fileId, mode);
}
} catch (FuseUnavailableMountException | InterruptedException error) {
Slog.v(TAG, "The mount point has already been invalid", error);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f0b472b..a2cbfaa 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -47,6 +47,7 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.util.LocalLog;
import android.util.StatsLog;
@@ -1664,6 +1665,14 @@
}
@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
+ }
+
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
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/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index d1b56e9..e80e9e1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -105,6 +105,7 @@
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.graphics.allocator@2.0::IAllocator",
"android.hardware.graphics.composer@2.1::IComposer",
+ "android.hardware.health@2.0::IHealth",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
"android.hardware.sensors@1.0::ISensors",
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..23287cf 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1801,16 +1801,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 +2067,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/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 5c77f0a..8571ae6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
@@ -26,8 +28,6 @@
import java.io.PrintWriter;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
-
/**
* Settings constants that can modify the activity manager's behavior.
*/
@@ -222,6 +222,10 @@
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
volatile boolean mFlagActivityStartsLoggingEnabled;
+ // Indicates whether the background activity starts is enabled.
+ // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED
+ volatile boolean mFlagBackgroundActivityStartsEnabled;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -256,6 +260,10 @@
private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED);
+ private static final Uri BACKGROUND_ACTIVITY_STARTS_ENABLED_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED);
+
public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
super(handler);
mService = service;
@@ -266,8 +274,10 @@
mResolver = resolver;
mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);
mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
+ mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
updateConstants();
updateActivityStartsLoggingEnabled();
+ updateBackgroundActivityStartsEnabled();
}
public void setOverrideMaxCachedProcesses(int value) {
@@ -290,6 +300,8 @@
updateConstants();
} else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
updateActivityStartsLoggingEnabled();
+ } else if (BACKGROUND_ACTIVITY_STARTS_ENABLED_URI.equals(uri)) {
+ updateBackgroundActivityStartsEnabled();
}
}
@@ -373,6 +385,11 @@
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;
}
+ private void updateBackgroundActivityStartsEnabled() {
+ mFlagBackgroundActivityStartsEnabled = Settings.Global.getInt(mResolver,
+ Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, 1) == 1;
+ }
+
private void updateMaxCachedProcesses() {
CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e9e83c..c16f1db 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;
@@ -328,6 +329,7 @@
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
+import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
import com.android.server.IoThread;
import com.android.server.LocalServices;
@@ -497,6 +499,18 @@
private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
/**
+ * The number of binder proxies we need to have before we start warning and
+ * dumping debug info.
+ */
+ private static final int BINDER_PROXY_HIGH_WATERMARK = 6000;
+
+ /**
+ * Low watermark that needs to be met before we consider dumping info again,
+ * after already hitting the high watermark.
+ */
+ private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
+
+ /**
* State indicating that there is no need for any blocking for network.
*/
@VisibleForTesting
@@ -653,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);
}
@@ -1876,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();
}
@@ -2250,7 +2296,8 @@
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityTaskManager = atm;
- mActivityTaskManager.setActivityManagerService(mIntentFirewall, mPendingIntentController);
+ mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
+ DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mProcessCpuThread = new Thread("CpuTracker") {
@@ -4240,14 +4287,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");
@@ -4360,7 +4400,6 @@
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
- app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
app.forcingToImportant = null;
@@ -4567,6 +4606,10 @@
profilerInfo.closeFd();
profilerInfo = null;
}
+
+ // Make app active after binding application or client may be running requests (e.g
+ // starting activities) before it is ready.
+ app.makeActive(thread, mProcessStats);
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
mProcessList.updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
@@ -4765,8 +4808,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(
@@ -8477,7 +8520,8 @@
mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
- BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
+ BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
+ BINDER_PROXY_LOW_WATERMARK);
BinderInternal.nSetBinderProxyCountEnabled(true);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@@ -9165,6 +9209,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) {
@@ -9217,11 +9274,6 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpBinderProxies(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
dumpLmkLocked(pw);
}
pw.println();
@@ -9373,7 +9425,7 @@
cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
- dumpBinderProxies(pw);
+ dumpBinderProxies(pw, 0 /* minToDump */);
} else {
String uid = args[opti];
opti++;
@@ -9714,10 +9766,17 @@
return false;
}
- void dumpBinderProxies(PrintWriter pw) {
+ void dumpBinderProxies(PrintWriter pw, int minCountToDumpInterfaces) {
pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
- dumpBinderProxyInterfaceCounts(pw,
- " Top proxy interface names held by SYSTEM");
+ final int proxyCount = BinderProxy.getProxyCount();
+ if (proxyCount >= minCountToDumpInterfaces) {
+ dumpBinderProxyInterfaceCounts(pw,
+ "Top proxy interface names held by SYSTEM");
+ } else {
+ pw.print("Not dumping proxy interface counts because size ("
+ + Integer.toString(proxyCount) + ") looks reasonable");
+ pw.println();
+ }
dumpBinderProxiesCounts(pw,
" Counts of Binder Proxies held by SYSTEM");
}
@@ -10207,7 +10266,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++;
}
@@ -10219,7 +10279,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++) {
@@ -10317,7 +10379,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);
@@ -10347,14 +10409,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)
@@ -13080,11 +13134,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);
@@ -18881,7 +18932,6 @@
if (memoryStat == null) {
continue;
}
- // TODO(rslawik): Delete RSS high-water mark field.
ProcessMemoryState processMemoryState =
new ProcessMemoryState(uid,
r.processName,
@@ -18891,7 +18941,6 @@
memoryStat.rssInBytes,
memoryStat.cacheInBytes,
memoryStat.swapInBytes,
- memoryStat.rssHighWatermarkInBytes,
memoryStat.startTimeNanos);
processMemoryStates.add(processMemoryState);
}
@@ -19173,6 +19222,10 @@
return mConstants.mFlagActivityStartsLoggingEnabled;
}
+ public boolean isBackgroundActivityStartsEnabled() {
+ return mConstants.mFlagBackgroundActivityStartsEnabled;
+ }
+
public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
synchronized(ActivityManagerService.this) {
ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
@@ -19313,7 +19366,7 @@
@Override
public boolean isAppStorageSandboxed(int pid, int uid) {
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
return false;
}
if (uid == SHELL_UID || uid == ROOT_UID) {
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 4fe7d03..09df7e20 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -222,11 +222,11 @@
receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
boolean result = requestAutofillData
- ? ActivityTaskManager.getService().requestAssistContextExtras(
+ ? ActivityTaskManager.getService().requestAutofillData(this,
+ receiverExtras, topActivity, 0 /* flags */)
+ : ActivityTaskManager.getService().requestAssistContextExtras(
ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
- /* focused= */ i == 0, /* newSessionId= */ i == 0)
- : ActivityTaskManager.getService().requestAutofillData(this,
- receiverExtras, topActivity, 0 /* flags */);
+ /* focused= */ i == 0, /* newSessionId= */ i == 0);
if (result) {
mPendingDataCount++;
} else if (i == 0) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 2541352..24543b7 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -25,10 +25,12 @@
import android.net.wifi.WifiActivityEnergyInfo;
import android.os.BatteryStats;
import android.os.Parcelable;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
+import android.os.ThreadLocalWorkSource;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.IntArray;
@@ -43,11 +45,9 @@
import libcore.util.EmptyArray;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -74,7 +74,12 @@
private final ScheduledExecutorService mExecutorService =
Executors.newSingleThreadScheduledExecutor(
(ThreadFactory) r -> {
- Thread t = new Thread(r, "batterystats-worker");
+ Thread t = new Thread(
+ () -> {
+ ThreadLocalWorkSource.setUid(Process.myUid());
+ r.run();
+ },
+ "batterystats-worker");
t.setPriority(Thread.NORM_PRIORITY);
return t;
});
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a0977be..8c39d75 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -58,8 +58,9 @@
/**
* BROADCASTS
*
- * We keep two broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts.
+ * We keep three broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts, and one to
+ * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
*/
public final class BroadcastQueue {
private static final String TAG = "BroadcastQueue";
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index cc3da1c..90fe30c 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -49,13 +49,8 @@
private static final boolean DEVICE_HAS_PER_APP_MEMCG =
SystemProperties.getBoolean("ro.config.per_app_memcg", false);
- /** Path to check if device has memcg */
- private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
/** Path to memory stat file for logging app start memory state */
private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
- /** Path to memory max usage file for logging app memory state */
- private static final String MEMORY_MAX_USAGE_FILE_FMT =
- "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes";
/** Path to procfs stat file for logging app start memory state */
private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
/** Path to procfs status file for logging app memory state */
@@ -98,14 +93,7 @@
@Nullable
static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
- MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath));
- if (stat == null) {
- return null;
- }
- String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid);
- stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg(
- readFileContents(maxUsagePath));
- return stat;
+ return parseMemoryStatFromMemcg(readFileContents(statPath));
}
/**
@@ -116,12 +104,7 @@
@Nullable
public static MemoryStat readMemoryStatFromProcfs(int pid) {
final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
- MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
- if (stat == null) {
- return null;
- }
- stat.rssHighWatermarkInBytes = readRssHighWaterMarkFromProcfs(pid);
- return stat;
+ return parseMemoryStatFromProcfs(readFileContents(statPath));
}
/**
@@ -185,19 +168,6 @@
return memoryStat;
}
- @VisibleForTesting
- static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) {
- if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) {
- return 0;
- }
- try {
- return Long.parseLong(memoryMaxUsageContents);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Failed to parse value", e);
- return 0;
- }
- }
-
/**
* Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs.
*/
@@ -258,8 +228,6 @@
public long cacheInBytes;
/** Number of bytes of swap usage */
public long swapInBytes;
- /** Number of bytes of peak anonymous and swap cache memory */
- public long rssHighWatermarkInBytes;
/** Device time when the processes started. */
public long startTimeNanos;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4b19398..8cf9f1e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -27,7 +27,6 @@
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
-import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -73,6 +72,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.text.TextUtils;
import android.util.EventLog;
@@ -537,40 +537,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 +1250,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);
}
@@ -1281,8 +1283,7 @@
final IPackageManager pm = AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName,
MATCH_DIRECT_BOOT_AUTO, app.userId);
- if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)
- && mountExtStorageFull) {
+ if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {
mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
} else {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
@@ -1768,8 +1769,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;
@@ -1929,10 +1930,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);
@@ -2242,6 +2241,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()
@@ -2354,91 +2538,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);
@@ -2474,6 +2598,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/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a5848ca..4c4a090 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -65,6 +65,7 @@
// permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sGlobalSettings = new String[] {
+ Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
};
@VisibleForTesting
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index b817669..48b4145 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -1,17 +1,6 @@
{
"presubmit": [
{
- "name": "CtsActivityManagerDeviceTestCases",
- "options": [
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "CtsActivityManagerDeviceSdk25TestCases",
"options": [
{
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d27c9..6cde4ad 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -649,6 +649,9 @@
private String mEnabledSurroundFormats;
private boolean mSurroundModeChanged;
+ @GuardedBy("mSettingsLock")
+ private int mAssistantUid;
+
// Intent "extra" data keys.
public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
public static final String CONNECT_INTENT_KEY_STATE = "state";
@@ -1079,6 +1082,10 @@
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock);
sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
sendEnabledSurroundFormats(mContentResolver, true);
+ updateAssistantUId(true);
+ }
+ synchronized (mAccessibilityServiceUidsLock) {
+ AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
}
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiTvClient != null) {
@@ -1404,6 +1411,39 @@
}
}
+ @GuardedBy("mSettingsLock")
+ private void updateAssistantUId(boolean forceUpdate) {
+ int assistantUid = 0;
+
+ // Consider assistants in the following order of priority:
+ // 1) voice interaction service
+ // 2) assistant
+ String assistantName = Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT);
+ if (TextUtils.isEmpty(assistantName)) {
+ assistantName = Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
+ }
+ if (!TextUtils.isEmpty(assistantName)) {
+ String packageName = ComponentName.unflattenFromString(assistantName).getPackageName();
+ if (!TextUtils.isEmpty(packageName)) {
+ try {
+ assistantUid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG,
+ "updateAssistantUId() could not find UID for package: " + packageName);
+ }
+ }
+ }
+
+ if (assistantUid != mAssistantUid || forceUpdate) {
+ AudioSystem.setAssistantUid(assistantUid);
+ mAssistantUid = assistantUid;
+ }
+ }
+
private void readPersistedSettings() {
final ContentResolver cr = mContentResolver;
@@ -1447,6 +1487,7 @@
readDockAudioSettings(cr);
sendEncodedSurroundMode(cr, "readPersistedSettings");
sendEnabledSurroundFormats(cr, true);
+ updateAssistantUId(true);
}
mMuteAffectedStreams = System.getIntForUser(cr,
@@ -5811,6 +5852,9 @@
mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
+
+ mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
}
@Override
@@ -5832,6 +5876,7 @@
updateMasterMono(mContentResolver);
updateEncodedSurroundOutput();
sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
+ updateAssistantUId(false);
}
}
@@ -7658,6 +7703,7 @@
mAccessibilityServiceUids = uids.toArray();
}
}
+ AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 2c2d404..eaa7a83 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -19,19 +19,11 @@
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.security.KeyStore;
-import android.text.TextUtils;
import android.util.Slog;
-import com.android.internal.statusbar.IStatusBarService;
-
import java.util.ArrayList;
/**
@@ -39,88 +31,15 @@
*/
public abstract class AuthenticationClient extends ClientMonitor {
private long mOpId;
- private Handler mHandler;
public abstract int handleFailedAttempt();
public abstract void resetFailedAttempts();
- public abstract String getErrorString(int error, int vendorCode);
- public abstract String getAcquiredString(int acquireInfo, int vendorCode);
- /**
- * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
- */
- public abstract int getBiometricType();
public static final int LOCKOUT_NONE = 0;
public static final int LOCKOUT_TIMED = 1;
public static final int LOCKOUT_PERMANENT = 2;
private final boolean mRequireConfirmation;
- // Callback mechanism received from the client
- // (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
- private IBiometricPromptReceiver mDialogReceiverFromClient;
- private Bundle mBundle;
- private IStatusBarService mStatusBarService;
- private boolean mInLockout;
- private TokenEscrow mEscrow;
- protected boolean mDialogDismissed;
-
- /**
- * Container that holds the identifier and authToken. For biometrics that require user
- * confirmation, these should not be sent to their final destinations until the user confirms.
- */
- class TokenEscrow {
- final BiometricAuthenticator.Identifier mIdentifier;
- final ArrayList<Byte> mToken;
-
- TokenEscrow(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
- mIdentifier = identifier;
- mToken = token;
- }
-
- BiometricAuthenticator.Identifier getIdentifier() {
- return mIdentifier;
- }
-
- ArrayList<Byte> getToken() {
- return mToken;
- }
- }
-
- // Receives events from SystemUI and handles them before forwarding them to BiometricDialog
- protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
- @Override // binder call
- public void onDialogDismissed(int reason) {
- if (mBundle != null && mDialogReceiverFromClient != null) {
- try {
- if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Positive button is used by passive modalities as a "confirm" button,
- // do not send to client
- mDialogReceiverFromClient.onDialogDismissed(reason);
- }
- if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- 0 /* vendorCode */);
- } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Have the service send the token to KeyStore, and send onAuthenticated
- // to the application.
- if (mEscrow != null) {
- if (DEBUG) Slog.d(getLogTag(), "Confirmed");
- addTokenToKeyStore(mEscrow.getToken());
- notifyClientAuthenticationSucceeded(mEscrow.getIdentifier());
- mEscrow = null;
- onAuthenticationConfirmed();
- } else {
- Slog.e(getLogTag(), "Escrow is null!!!");
- }
- }
- mDialogDismissed = true;
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception", e);
- }
- stop(true /* initiatedByClient */);
- }
- }
- };
/**
* This method is called when authentication starts.
@@ -133,25 +52,13 @@
*/
public abstract void onStop();
- /**
- * This method is called when biometric authentication was confirmed by the user. The client
- * should be removed.
- */
- public abstract void onAuthenticationConfirmed();
-
public AuthenticationClient(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- boolean requireConfirmation) {
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
- restricted, owner);
+ restricted, owner, cookie);
mOpId = opId;
- mBundle = bundle;
- mDialogReceiverFromClient = dialogReceiver;
- mStatusBarService = statusBarService;
- mHandler = new Handler(Looper.getMainLooper());
mRequireConfirmation = requireConfirmation;
}
@@ -164,175 +71,99 @@
stop(false /* initiatedByClient */);
}
- @Override
- public boolean onAcquired(int acquiredInfo, int vendorCode) {
- // If the dialog is showing, the client doesn't need to receive onAcquired messages.
- if (mBundle != null) {
- try {
- if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
- }
- return false; // acquisition continues
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
- return true; // client failed
- } finally {
- // Good scans will keep the device awake
- if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- notifyUserActivity();
- }
- }
- } else {
- return super.onAcquired(acquiredInfo, vendorCode);
- }
- }
-
- @Override
- public boolean onError(long deviceId, int error, int vendorCode) {
- if (mDialogDismissed) {
- // If user cancels authentication, the application has already received the
- // ERROR_USER_CANCELED message from onDialogDismissed()
- // and stopped the biometric hardware, so there is no need to send a
- // ERROR_CANCELED message.
- return true;
- }
- if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
- try {
- mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception when sending error", e);
- }
- }
- return super.onError(deviceId, error, vendorCode);
- }
-
- public void setTitleIfEmpty(CharSequence title) {
- if (TextUtils.isEmpty(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
- mBundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
- }
- }
-
public boolean isBiometricPrompt() {
- return mBundle != null;
+ return getCookie() != 0;
}
- private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier)
- throws RemoteException {
- final BiometricServiceBase.ServiceListener listener = getListener();
- // Explicitly have if/else here to make it super obvious in case the code is
- // touched in the future.
- if (!getIsRestricted()) {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), identifier, getTargetUserId());
- } else {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), null, getTargetUserId());
- }
- }
-
- private void addTokenToKeyStore(ArrayList<Byte> token) {
- // Send the token to KeyStore
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
- }
- KeyStore.getInstance().addAuthToken(byteToken);
+ public boolean getRequireConfirmation() {
+ return mRequireConfirmation;
}
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
- if (authenticated) {
- mAlreadyDone = true;
- if (mRequireConfirmation) {
- // Store the token so it can be sent to keystore after the user presses confirm
- mEscrow = new TokenEscrow(identifier, token);
- } else {
- addTokenToKeyStore(token);
- }
- }
+ final BiometricServiceBase.ServiceListener listener = getListener();
+ mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
boolean result = false;
- // If the biometric dialog is showing, notify authentication succeeded
- if (mBundle != null) {
- try {
- if (authenticated) {
- mStatusBarService.onBiometricAuthenticated();
- } else {
- mStatusBarService.onBiometricHelp(getContext().getResources().getString(
- com.android.internal.R.string.biometric_not_recognized));
+ try {
+ if (authenticated) {
+ mAlreadyDone = true;
+ if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + getOwnerString()
+ + ", ID:" + identifier.getBiometricId()
+ + ", isBP: " + isBiometricPrompt()
+ + ", listener: " + listener
+ + ", requireConfirmation: " + mRequireConfirmation);
+ if (listener != null) {
+ vibrateSuccess();
}
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
- }
- }
+ result = true;
+ resetFailedAttempts();
+ onStop();
- final BiometricServiceBase.ServiceListener listener = getListener();
- if (listener != null) {
- try {
- mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
- if (!authenticated) {
- listener.onAuthenticationFailed(getHalDeviceId());
- } else {
- if (DEBUG) {
- Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
- + ", id=" + identifier.getBiometricId());
- }
- if (!mRequireConfirmation) {
- notifyClientAuthenticationSucceeded(identifier);
- }
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
}
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
- result = true; // client failed
- }
- } else {
- result = true; // client not listening
- }
- if (!authenticated) {
- if (listener != null) {
- vibrateError();
- }
- // allow system-defined limit of number of attempts before giving up
- int lockoutMode = handleFailedAttempt();
- if (lockoutMode != LOCKOUT_NONE) {
- try {
- mInLockout = true;
- Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
- lockoutMode + ")");
+ if (isBiometricPrompt() && listener != null) {
+ // BiometricService will add the token to keystore
+ listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
+ } else if (!isBiometricPrompt() && listener != null) {
+ KeyStore.getInstance().addAuthToken(byteToken);
+ try {
+ // Explicitly have if/else here to make it super obvious in case the code is
+ // touched in the future.
+ if (!getIsRestricted()) {
+ listener.onAuthenticationSucceeded(
+ getHalDeviceId(), identifier, getTargetUserId());
+ } else {
+ listener.onAuthenticationSucceeded(
+ getHalDeviceId(), null, getTargetUserId());
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "Remote exception", e);
+ }
+ } else {
+ // Client not listening
+ Slog.w(getLogTag(), "Client not listening");
+ result = true;
+ }
+ } else {
+ if (listener != null) {
+ vibrateError();
+ }
+ // Allow system-defined limit of number of attempts before giving up
+ final int lockoutMode = handleFailedAttempt();
+ if (lockoutMode != LOCKOUT_NONE) {
+ Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+ + lockoutMode + ")");
stop(false);
- int errorCode = lockoutMode == LOCKOUT_TIMED ?
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-
- // Send the lockout message to the system dialog
- if (mBundle != null) {
- mStatusBarService.onBiometricError(
- getErrorString(errorCode, 0 /* vendorCode */));
- mHandler.postDelayed(() -> {
- try {
- listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "RemoteException while sending error");
- }
- }, BiometricPrompt.HIDE_DIALOG_DELAY);
- } else {
- listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+ final int errorCode = lockoutMode == LOCKOUT_TIMED
+ ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (listener != null) {
+ listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */,
+ getCookie());
}
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "Failed to notify lockout:", e);
+ } else {
+ // Don't send onAuthenticationFailed if we're in lockout, it causes a
+ // janky UI on Keyguard/BiometricPrompt since "authentication failed"
+ // will show briefly and be replaced by "device locked out" message.
+ if (listener != null) {
+ if (isBiometricPrompt()) {
+ listener.onAuthenticationFailedInternal(getCookie(),
+ getRequireConfirmation());
+ } else {
+ listener.onAuthenticationFailed(getHalDeviceId());
+ }
+ }
}
+ result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
}
- result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
- } else {
- if (listener != null) {
- vibrateSuccess();
- }
- // we have a valid biometric that doesn't require confirmation, done
- result |= !mRequireConfirmation;
- resetFailedAttempts();
- onStop();
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "Remote exception", e);
+ result = true;
}
return result;
}
@@ -353,16 +184,6 @@
return result;
}
if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
-
- // If authenticating with system dialog, show the dialog
- if (mBundle != null) {
- try {
- mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- getBiometricType(), mRequireConfirmation, getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to show biometric dialog", e);
- }
- }
} catch (RemoteException e) {
Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
@@ -390,18 +211,6 @@
} catch (RemoteException e) {
Slog.e(getLogTag(), "stopAuthentication failed", e);
return ERROR_ESRCH;
- } finally {
- // If the user already cancelled authentication (via some interaction with the
- // dialog, we do not need to hide it since it's already hidden.
- // If the device is in lockout, don't hide the dialog - it will automatically hide
- // after BiometricPrompt.HIDE_DIALOG_DELAY
- if (mBundle != null && !mDialogDismissed && !mInLockout) {
- try {
- mStatusBarService.hideBiometricDialog();
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to hide biometric dialog", e);
- }
- }
}
mAlreadyCancelled = true;
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 5f09189..1882be2 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -19,9 +19,17 @@
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,9 +40,9 @@
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
@@ -50,14 +58,21 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.security.KeyStore;
+import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Random;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -66,32 +81,10 @@
private static final String TAG = "BiometricService";
- /**
- * No biometric methods or nothing has been enrolled.
- * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
- * modalities when calling authenticate().
- */
- private static final int BIOMETRIC_NONE = 0;
-
- /**
- * Constant representing fingerprint.
- */
- private static final int BIOMETRIC_FINGERPRINT = 1 << 0;
-
- /**
- * Constant representing iris.
- */
- private static final int BIOMETRIC_IRIS = 1 << 1;
-
- /**
- * Constant representing face.
- */
- private static final int BIOMETRIC_FACE = 1 << 2;
-
private static final int[] FEATURE_ID = {
- BIOMETRIC_FINGERPRINT,
- BIOMETRIC_IRIS,
- BIOMETRIC_FACE
+ TYPE_FINGERPRINT,
+ TYPE_IRIS,
+ TYPE_FACE
};
private final AppOpsManager mAppOps;
@@ -242,10 +235,367 @@
*/
private final class BiometricServiceWrapper extends IBiometricService.Stub {
+ /**
+ * Authentication either just called and we have not transitioned to the CALLED state, or
+ * authentication terminated (success or error).
+ */
+ private static final int STATE_AUTH_IDLE = 0;
+ /**
+ * Authentication was called and we are waiting for the <Biometric>Services to return their
+ * cookies before starting the hardware and showing the BiometricPrompt.
+ */
+ private static final int STATE_AUTH_CALLED = 1;
+ /**
+ * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+ */
+ private static final int STATE_AUTH_STARTED = 2;
+ /**
+ * Authentication is paused, waiting for the user to press "try again" button. Since the
+ * try again button requires us to cancel authentication, this represents the state where
+ * ERROR_CANCELED is not received yet.
+ */
+ private static final int STATE_AUTH_PAUSED = 3;
+ /**
+ * Same as above, except the ERROR_CANCELED has been received.
+ */
+ private static final int STATE_AUTH_PAUSED_CANCELED = 4;
+ /**
+ * Authentication is successful, but we're waiting for the user to press "confirm" button.
+ */
+ private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+
+ final class AuthSession {
+ // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
+ // <Biometric>Services before we can start authenticating. Pairs that have been returned
+ // are moved to mModalitiesMatched.
+ final HashMap<Integer, Integer> mModalitiesWaiting;
+ // Pairs that have been matched.
+ final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
+
+ // The following variables are passed to authenticateInternal, which initiates the
+ // appropriate <Biometric>Services.
+ final IBinder mToken;
+ final long mSessionId;
+ final int mUserId;
+ // Original receiver from BiometricPrompt.
+ final IBiometricServiceReceiver mClientReceiver;
+ final String mOpPackageName;
+ // Info to be shown on BiometricDialog when all cookies are returned.
+ final Bundle mBundle;
+ final int mCallingUid;
+ final int mCallingPid;
+ final int mCallingUserId;
+ // Continue authentication with the same modality/modalities after "try again" is
+ // pressed
+ final int mModality;
+
+ // The current state, which can be either idle, called, or started
+ private int mState = STATE_AUTH_IDLE;
+ // For explicit confirmation, do not send to keystore until the user has confirmed
+ // the authentication.
+ byte[] mTokenEscrow;
+
+ AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiver receiver, String opPackageName,
+ Bundle bundle, int callingUid, int callingPid, int callingUserId,
+ int modality) {
+ mModalitiesWaiting = modalities;
+ mToken = token;
+ mSessionId = sessionId;
+ mUserId = userId;
+ mClientReceiver = receiver;
+ mOpPackageName = opPackageName;
+ mBundle = bundle;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ mCallingUserId = callingUserId;
+ mModality = modality;
+ }
+
+ boolean containsCookie(int cookie) {
+ if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
+ return true;
+ }
+ if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (mCurrentAuthSession != null
+ && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)
+ && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ // We only care about this state, since <Biometric>Service will
+ // cancel any client that's still in STATE_AUTH_STARTED
+ mStatusBarService.hideBiometricDialog();
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_canceled)
+ );
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get running tasks", e);
+ }
+ }
+ }
+
+ private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService(
+ ActivityTaskManager.class).getService();
+ private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ private final BiometricTaskStackListener mTaskStackListener =
+ new BiometricTaskStackListener();
+ private final Random mRandom = new Random();
+
+ // The current authentication session, null if idle/done. We need to track both the current
+ // and pending sessions since errors may be sent to either.
+ private AuthSession mCurrentAuthSession;
+ private AuthSession mPendingAuthSession;
+
+ // Wrap the client's receiver so we can do things with the BiometricDialog first
+ private final IBiometricServiceReceiverInternal mInternalReceiver =
+ new IBiometricServiceReceiverInternal.Stub() {
+ @Override
+ public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ try {
+ if (!requireConfirmation) {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ KeyStore.getInstance().addAuthToken(token);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } else {
+ // Store the auth token and submit it to keystore after the confirmation
+ // button has been pressed.
+ mCurrentAuthSession.mTokenEscrow = token;
+ mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
+ }
+
+ // Notify SysUI that the biometric has been authenticated. SysUI already knows
+ // the implicit/explicit state and will react accordingly.
+ mStatusBarService.onBiometricAuthenticated();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ try {
+ mStatusBarService.onBiometricHelp(getContext().getResources().getString(
+ com.android.internal.R.string.biometric_not_recognized));
+ if (requireConfirmation) {
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+ mStatusBarService.showBiometricTryAgain();
+ // Cancel authentication. Skip the token/package check since we are
+ // cancelling from system server. The interface is permission protected so
+ // this is fine.
+ cancelInternal(null /* token */, null /* package */,
+ false /* fromClient */);
+ }
+ mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onError(int cookie, int error, String message) throws RemoteException {
+ Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+ // Errors can either be from the current auth session or the pending auth session.
+ // The pending auth session may receive errors such as ERROR_LOCKOUT before
+ // it becomes the current auth session. Similarly, the current auth session may
+ // receive errors such as ERROR_CANCELED while the pending auth session is preparing
+ // to be started. Thus we must match error messages with their cookies to be sure
+ // of their intended receivers.
+ try {
+ if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } else {
+ // Send errors after the dialog is dismissed.
+ mHandler.postDelayed(() -> {
+ try {
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }, BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+ } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+ || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+ && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ // Skip the first ERROR_CANCELED message when this happens, since
+ // "try again" requires us to cancel authentication but keep
+ // the prompt showing.
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
+ } else {
+ // In the "try again" state, we should forward canceled errors to
+ // the client and and clean up.
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ } else {
+ Slog.e(TAG, "Impossible session error state: "
+ + mCurrentAuthSession.mState);
+ }
+ } else if (mPendingAuthSession != null
+ && mPendingAuthSession.containsCookie(cookie)) {
+ if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
+ mPendingAuthSession.mClientReceiver.onError(error, message);
+ mPendingAuthSession.mState = STATE_AUTH_IDLE;
+ mPendingAuthSession = null;
+ } else {
+ Slog.e(TAG, "Impossible pending session error state: "
+ + mPendingAuthSession.mState);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+ if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+ try {
+ mStatusBarService.onBiometricHelp(message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
+ if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Positive button is used by passive modalities as a "confirm" button,
+ // do not send to client
+ mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+ }
+ if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled));
+ } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Have the service send the token to KeyStore, and send onAuthenticated
+ // to the application
+ KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ }
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ Slog.d(TAG, "onTryAgainPressed");
+ // No need to check permission, since it can only be invoked by SystemUI
+ // (or system server itself).
+ mHandler.post(() -> {
+ authenticateInternal(mCurrentAuthSession.mToken,
+ mCurrentAuthSession.mSessionId,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mClientReceiver,
+ mCurrentAuthSession.mOpPackageName,
+ mCurrentAuthSession.mBundle,
+ mCurrentAuthSession.mCallingUid,
+ mCurrentAuthSession.mCallingPid,
+ mCurrentAuthSession.mCallingUserId,
+ mCurrentAuthSession.mModality);
+ });
+ }
+ };
+
+ @Override // Binder call
+ public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
+ checkInternalPermission();
+
+ Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getValue() == cookie) {
+ mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
+ mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
+ Slog.d(TAG, "Matched cookie: " + cookie + ", "
+ + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
+ break;
+ }
+ }
+
+ if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
+ final boolean mContinuing = mCurrentAuthSession != null
+ && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
+ mCurrentAuthSession = mPendingAuthSession;
+ mPendingAuthSession = null;
+
+ mCurrentAuthSession.mState = STATE_AUTH_STARTED;
+ try {
+ int modality = TYPE_NONE;
+ it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getKey() == TYPE_FINGERPRINT) {
+ mFingerprintService.startPreparedClient(pair.getValue());
+ } else if (pair.getKey() == TYPE_IRIS) {
+ Slog.e(TAG, "Iris unsupported");
+ } else if (pair.getKey() == TYPE_FACE) {
+ mFaceService.startPreparedClient(pair.getValue());
+ } else {
+ Slog.e(TAG, "Unknown modality: " + pair.getKey());
+ }
+ modality |= pair.getKey();
+ }
+
+ if (!mContinuing) {
+ mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+ mInternalReceiver, modality, requireConfirmation, userId);
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
@Override // Binder call
public void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
+ throws RemoteException {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -261,16 +611,45 @@
checkInternalPermission();
}
- if (token == null || receiver == null || opPackageName == null || bundle == null
- || dialogReceiver == null) {
+ if (token == null || receiver == null || opPackageName == null || bundle == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
}
// Check the usage of this in system server. Need to remove this check if it becomes
// a public API.
- if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+ final boolean useDefaultTitle =
+ bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
+ if (useDefaultTitle) {
checkInternalPermission();
+ // Set the default title if necessary
+ try {
+ if (useDefaultTitle) {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ for (int i = 0; i < procs.size(); i++) {
+ final ActivityManager.RunningAppProcessInfo info = procs.get(i);
+ if (info.uid == callingUid
+ && info.importance == IMPORTANCE_FOREGROUND) {
+ PackageManager pm = getContext().getPackageManager();
+ final CharSequence label = pm.getApplicationLabel(
+ pm.getApplicationInfo(info.processName,
+ PackageManager.GET_META_DATA));
+ final String title = getContext()
+ .getString(R.string.biometric_dialog_default_title, label);
+ if (TextUtils.isEmpty(
+ bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
+ }
+ break;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Name not found", e);
+ }
}
mHandler.post(() -> {
@@ -285,13 +664,13 @@
getContext().getString(R.string.biometric_error_hw_unavailable);
switch (error) {
case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
- receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ receiver.onError(error, hardwareUnavailable);
break;
case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
- receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ receiver.onError(error, hardwareUnavailable);
break;
case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
- receiver.onError(0 /* deviceId */, error,
+ receiver.onError(error,
getErrorString(modality, error, 0 /* vendorCode */));
break;
default:
@@ -304,60 +683,91 @@
return;
}
- // Actually start authentication
mCurrentModality = modality;
- try {
- // No polymorphism :(
- if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
- mFingerprintService.authenticateFromService(token, sessionId, userId,
- receiver, flags, opPackageName, bundle, dialogReceiver,
- callingUid, callingPid, callingUserId);
- } else if (mCurrentModality == BIOMETRIC_IRIS) {
- Slog.w(TAG, "Unsupported modality");
- } else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.authenticateFromService(true /* requireConfirmation */,
- token, sessionId, userId, receiver, flags, opPackageName,
- bundle, dialogReceiver, callingUid, callingPid, callingUserId);
- } else {
- Slog.w(TAG, "Unsupported modality");
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to start authentication", e);
- }
+
+ // Actually start authentication
+ authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
+ callingUid, callingPid, callingUserId, modality);
});
}
+ /**
+ * authenticate() (above) which is called from BiometricPrompt determines which
+ * modality/modalities to start authenticating with. authenticateInternal() should only be
+ * used for:
+ * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
+ * invoked, shortly after which BiometricPrompt is shown and authentication starts
+ * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
+ * and the user has pressed "try again"
+ */
+ private void authenticateInternal(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
+ int callingUid, int callingPid, int callingUserId, int modality) {
+ try {
+ // Generate random cookies to pass to the services that should prepare to start
+ // authenticating. Store the cookie here and wait for all services to "ack"
+ // with the cookie. Once all cookies are received, we can show the prompt
+ // and let the services start authenticating. The cookie should be non-zero.
+ final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+ Slog.d(TAG, "Creating auth session. Modality: " + modality
+ + ", cookie: " + cookie);
+ final HashMap<Integer, Integer> authenticators = new HashMap<>();
+ authenticators.put(modality, cookie);
+ mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
+ receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+ modality);
+ mPendingAuthSession.mState = STATE_AUTH_CALLED;
+ // No polymorphism :(
+ if ((modality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.prepareForAuthentication(token, sessionId, userId,
+ mInternalReceiver, opPackageName, cookie,
+ callingUid, callingPid, callingUserId);
+ }
+ if ((modality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((modality & TYPE_FACE) != 0) {
+ mFaceService.prepareForAuthentication(true /* requireConfirmation */,
+ token, sessionId, userId, mInternalReceiver, opPackageName,
+ cookie, callingUid, callingPid, callingUserId);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start authentication", e);
+ }
+ }
+
@Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
checkPermission();
-
if (token == null || opPackageName == null) {
Slog.e(TAG, "Unable to cancel, one or more null arguments");
return;
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
+ // We need to check the current authenticators state. If we're pending confirm
+ // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
+ // since we won't be getting an onError from the driver.
+ if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ mHandler.post(() -> {
+ try {
+ // Send error to client
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled)
+ );
- mHandler.post(() -> {
- try {
- if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
- mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId);
- } else if (mCurrentModality == BIOMETRIC_IRIS) {
- Slog.w(TAG, "Unsupported modality");
- } else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId);
- } else {
- Slog.w(TAG, "Unsupported modality");
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
- }
- });
+ });
+ } else {
+ cancelInternal(token, opPackageName, true /* fromClient */);
+ }
}
@Override // Binder call
@@ -402,6 +812,49 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ @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();
+ final int callingUserId = UserHandle.getCallingUserId();
+ mHandler.post(() -> {
+ try {
+ // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+ // drivers have canceled authentication.
+ if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ if ((mCurrentModality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((mCurrentModality & TYPE_FACE) != 0) {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
+ });
+ }
}
private void checkAppOp(String opPackageName, int callingUid) {
@@ -413,7 +866,7 @@
}
private void checkInternalPermission() {
- getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+ getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
"Must have USE_BIOMETRIC_INTERNAL permission");
}
@@ -490,16 +943,19 @@
* returns errors through the callback (no biometric feature, hardware not detected, no
* templates enrolled, etc). This service must not start authentication if errors are sent.
*
- * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
- * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+ * @Returns A pair [Modality, Error] with Modality being one of
+ * {@link BiometricAuthenticator#TYPE_NONE},
+ * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
+ * {@link BiometricAuthenticator#TYPE_IRIS},
+ * {@link BiometricAuthenticator#TYPE_FACE}
* and the error containing one of the {@link BiometricConstants} errors.
*/
private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
- int modality = BIOMETRIC_NONE;
+ int modality = TYPE_NONE;
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
}
// Assuming that authenticators are listed in priority-order, the rest of this function
@@ -512,13 +968,13 @@
boolean hasTemplatesEnrolled = false;
boolean enabledForApps = false;
- int firstHwAvailable = BIOMETRIC_NONE;
+ int firstHwAvailable = TYPE_NONE;
for (int i = 0; i < mAuthenticators.size(); i++) {
modality = mAuthenticators.get(i).getType();
BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
- if (firstHwAvailable == BIOMETRIC_NONE) {
+ if (firstHwAvailable == TYPE_NONE) {
// Store the first one since we want to return the error in correct priority
// order.
firstHwAvailable = modality;
@@ -538,13 +994,13 @@
// Check error conditions
if (!isHardwareDetected) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
} else if (!hasTemplatesEnrolled) {
// Return the modality here so the correct error string can be sent. This error is
// preferred over !enabledForApps
return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
} else if (!enabledForApps) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
@@ -552,11 +1008,11 @@
private boolean isEnabledForApp(int modality) {
switch(modality) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return true;
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return true;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return mSettingObserver.getFaceEnabledForApps();
default:
Slog.w(TAG, "Unsupported modality: " + modality);
@@ -566,12 +1022,12 @@
private String getErrorString(int type, int error, int vendorCode) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
Slog.w(TAG, "Modality not supported");
return null; // not supported
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return FaceManager.getErrorString(getContext(), error, vendorCode);
default:
Slog.w(TAG, "Unable to get error string for modality: " + type);
@@ -581,12 +1037,12 @@
private BiometricAuthenticator getAuthenticator(int type) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return (FingerprintManager)
getContext().getSystemService(Context.FINGERPRINT_SERVICE);
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return null;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return (FaceManager)
getContext().getSystemService(Context.FACE_SERVICE);
default:
@@ -596,11 +1052,11 @@
private boolean hasFeature(int type) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return mHasFeatureFingerprint;
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return mHasFeatureIris;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return mHasFeatureFace;
default:
return false;
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 74d742a..9649ccd 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import android.app.ActivityManager;
@@ -36,8 +35,9 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.fingerprint.Fingerprint;
import android.os.Binder;
import android.os.Bundle;
@@ -56,7 +56,6 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
@@ -106,6 +105,7 @@
protected final AppOpsManager mAppOps;
protected final H mHandler = new H();
+ private IBiometricService mBiometricService;
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
@@ -223,12 +223,9 @@
public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver,
- IStatusBarService statusBarService, boolean requireConfirmation) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener,
- targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService, requireConfirmation);
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
+ groupId, opId, restricted, owner, cookie, requireConfirmation);
}
@Override
@@ -279,11 +276,6 @@
}
return AuthenticationClient.LOCKOUT_NONE;
}
-
- @Override
- public void onAuthenticationConfirmed() {
- removeClient(mCurrentClient);
- }
}
protected class EnrollClientImpl extends EnrollClient {
@@ -345,18 +337,28 @@
default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
- void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
- throws RemoteException;
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
- void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId)
- throws RemoteException;
+ default void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
- void onAuthenticationFailed(long deviceId)
- throws RemoteException;
+ default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
- void onError(long deviceId, int error, int vendorCode)
- throws RemoteException;
+ default void onAuthenticationFailed(long deviceId) throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
default void onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
@@ -366,6 +368,37 @@
}
/**
+ * Wraps the callback interface from Service -> BiometricPrompt
+ */
+ protected abstract class BiometricServiceListener implements ServiceListener {
+ private IBiometricServiceReceiverInternal mWrapperReceiver;
+
+ public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
+ mWrapperReceiver = wrapperReceiver;
+ }
+
+ public IBiometricServiceReceiverInternal getWrapperReceiver() {
+ return mWrapperReceiver;
+ }
+
+ @Override
+ public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+ }
+ }
+ }
+
+ /**
* Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
* subclasses.
*/
@@ -706,30 +739,6 @@
}
mHandler.post(() -> {
- if (client.isBiometricPrompt()) {
- try {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- for (int i = 0; i < procs.size(); i++) {
- final ActivityManager.RunningAppProcessInfo info = procs.get(i);
- if (info.uid == callingUid && info.importance == IMPORTANCE_FOREGROUND) {
- PackageManager pm = getContext().getPackageManager();
- final CharSequence label = pm.getApplicationLabel(
- pm.getApplicationInfo(info.processName,
- PackageManager.GET_META_DATA));
- final String title = getContext()
- .getString(R.string.biometric_dialog_default_title, label);
- client.setTitleIfEmpty(title);
- break;
- }
- }
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to get application name", e);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(getTag(), "Unable to get application name", e);
- }
- }
-
mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
// Get performance stats object for this user.
@@ -751,29 +760,37 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
+ true /* fromClient */);
}
protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
- if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
- return;
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
+ if (fromClient) {
+ // Only check this if cancel was called from the client (app). If cancel was called
+ // from BiometricService, it means the dialog was dismissed due to user interaction.
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+ return;
+ }
}
mHandler.post(() -> {
ClientMonitor client = mCurrentClient;
if (client instanceof AuthenticationClient) {
- if (client.getToken() == token) {
- if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+ if (client.getToken() == token || !fromClient) {
+ if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
+ + ", fromClient: " + fromClient);
+ // If cancel was from BiometricService, it means the dialog was dismissed
+ // and authentication should be canceled.
client.stop(client.getToken() == token);
} else {
- if (DEBUG) Slog.v(getTag(), "can't stop client "
- + client.getOwnerString() + " since tokens don't match");
+ if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
+ + " since tokens don't match. fromClient: " + fromClient);
}
} else if (client != null) {
- if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+ if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
+ client.getOwnerString());
}
});
@@ -805,8 +822,7 @@
int lockoutMode = getLockoutMode();
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(getTag(), "In lockout mode(" + lockoutMode +
- ") ; disallowing authentication");
+ Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
@@ -919,7 +935,6 @@
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
-
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
@@ -940,17 +955,51 @@
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
} else if (newClient != null) {
- mCurrentClient = newClient;
- if (DEBUG) Slog.v(getTag(), "starting client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- notifyClientActiveCallbacks(true);
+ // For BiometricPrompt clients, do not start until
+ // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
+ // modalities are ready before initiating authentication.
+ if (newClient instanceof AuthenticationClient) {
+ AuthenticationClient client = (AuthenticationClient) newClient;
+ if (client.isBiometricPrompt()) {
+ if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
+ mCurrentClient = newClient;
+ if (mBiometricService == null) {
+ mBiometricService = IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+ }
+ try {
+ mBiometricService.onReadyForAuthentication(client.getCookie(),
+ client.getRequireConfirmation(), client.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception", e);
+ }
+ return;
+ }
+ }
- newClient.start();
+ // We are not a BiometricPrompt client, start the client immediately
+ mCurrentClient = newClient;
+ startCurrentClient(mCurrentClient.getCookie());
}
}
+ protected void startCurrentClient(int cookie) {
+ if (mCurrentClient == null) {
+ Slog.e(getTag(), "Trying to start null client!");
+ return;
+ }
+ if (DEBUG) Slog.v(getTag(), "starting client "
+ + mCurrentClient.getClass().getSuperclass().getSimpleName()
+ + "(" + mCurrentClient.getOwnerString() + ")"
+ + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
+ if (cookie != mCurrentClient.getCookie()) {
+ Slog.e(getTag(), "Mismatched cookie");
+ return;
+ }
+ notifyClientActiveCallbacks(true);
+ mCurrentClient.start();
+ }
+
protected void removeClient(ClientMonitor client) {
if (client != null) {
client.destroy();
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index a7ada2f..d19aff6 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -58,6 +58,9 @@
private IBinder mToken;
private BiometricServiceBase.ServiceListener mListener;
+ // Currently only used for authentication client. The cookie generated by BiometricService
+ // is never 0.
+ private final int mCookie;
protected final MetricsLogger mMetricsLogger;
protected final Metrics mMetrics;
@@ -80,7 +83,7 @@
public ClientMonitor(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
- boolean restricted, String owner) {
+ boolean restricted, String owner, int cookie) {
mContext = context;
mMetrics = metrics;
mDaemon = daemon;
@@ -91,6 +94,7 @@
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
+ mCookie = cookie;
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
mMetricsLogger = new MetricsLogger();
@@ -107,6 +111,10 @@
return mMetrics.logTag();
}
+ public int getCookie() {
+ return mCookie;
+ }
+
/**
* Contacts the biometric's HAL to start the client.
* @return 0 on success, errno from driver on failure
@@ -174,7 +182,7 @@
public boolean onError(long deviceId, int error, int vendorCode) {
try {
if (mListener != null) {
- mListener.onError(deviceId, error, vendorCode);
+ mListener.onError(deviceId, error, vendorCode, getCookie());
}
} catch (RemoteException e) {
Slog.w(getLogTag(), "Failed to invoke sendError", e);
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 76dc5a9..f858ef5 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -40,7 +40,7 @@
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
mBiometricUtils = utils;
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
}
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 47dc7ff..df6220c 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -34,7 +34,7 @@
BiometricServiceBase.ServiceListener listener, int groupId, int userId,
boolean restricted, String owner) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 15b3773..be233ec 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -37,7 +37,7 @@
BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId,
boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
mBiometricId = biometricId;
mBiometricUtils = utils;
}
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 7aa2e47..557af04 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -27,9 +27,8 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.biometrics.face.V1_0.Status;
@@ -38,7 +37,6 @@
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -50,7 +48,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.BiometricServiceBase;
@@ -89,27 +86,9 @@
public FaceAuthClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- boolean requireConfirmation) {
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted, owner, bundle, dialogReceiver, statusBarService,
- requireConfirmation);
- }
-
- @Override
- public String getErrorString(int error, int vendorCode) {
- return FaceManager.getErrorString(getContext(), error, vendorCode);
- }
-
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
- return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
- }
-
- @Override
- public int getBiometricType() {
- return BiometricAuthenticator.TYPE_FACE;
+ restricted, owner, cookie, requireConfirmation);
}
}
@@ -162,28 +141,33 @@
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- null /* bundle */, null /* dialogReceiver */, mStatusBarService,
- false /* requireConfirmation */);
+ 0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(boolean requireConfirmation, IBinder token, long opId,
- int groupId, IBiometricServiceReceiver receiver, int flags,
- String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId) {
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
+ int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
+ String opPackageName, int cookie, int callingUid, int callingPid,
+ int callingUserId) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
- new BiometricPromptServiceListenerImpl(receiver),
- mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */);
+ new BiometricPromptServiceListenerImpl(wrapperReceiver),
+ mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
+ true /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@Override // Binder call
+ public void startPreparedClient(int cookie) {
+ checkPermission(MANAGE_BIOMETRIC);
+ startCurrentClient(cookie);
+ }
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
cancelAuthenticationInternal(token, opPackageName);
@@ -191,10 +175,10 @@
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- cancelAuthenticationInternal(token, opPackageName,
- callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
}
@Override // Binder call
@@ -405,12 +389,9 @@
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* BiometricPrompt.
*/
- private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
- private IBiometricServiceReceiver mBiometricServiceReceiver;
-
- public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
- mBiometricServiceReceiver = receiver;
+ private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+ BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+ super(wrapperReceiver);
}
@Override
@@ -419,32 +400,18 @@
/**
* Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
*/
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAcquired(deviceId,
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAcquired(
FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
}
}
@Override
- public void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
- }
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
- }
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onError(deviceId, error,
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onError(cookie, error,
FaceManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -455,7 +422,6 @@
* the FaceManager.
*/
private class ServiceListenerImpl implements ServiceListener {
-
private IFaceServiceReceiver mFaceServiceReceiver;
public ServiceListenerImpl(IFaceServiceReceiver receiver) {
@@ -501,7 +467,8 @@
}
@Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
if (mFaceServiceReceiver != null) {
mFaceServiceReceiver.onError(deviceId, error, vendorCode);
}
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 b0b788f..6a5bc61 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -30,9 +30,8 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
@@ -42,7 +41,6 @@
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -55,7 +53,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
@@ -109,27 +106,10 @@
public FingerprintAuthClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean restricted, String owner, int cookie,
boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted, owner, bundle, dialogReceiver, statusBarService,
- requireConfirmation);
- }
-
- @Override
- public String getErrorString(int error, int vendorCode) {
- return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- }
-
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
- return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
- }
-
- @Override
- public int getBiometricType() {
- return BiometricAuthenticator.TYPE_FINGERPRINT;
+ restricted, owner, cookie, requireConfirmation);
}
}
@@ -182,38 +162,44 @@
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
- null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */);
+ mCurrentUserId, groupId, opId, restricted, opPackageName,
+ 0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(IBinder token, long opId, int groupId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId) {
+ public void prepareForAuthentication(IBinder token, long opId, int groupId,
+ IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
- new BiometricPromptServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService, false /* requireConfirmation */);
+ new BiometricPromptServiceListenerImpl(wrapperReceiver),
+ mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
+ false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@Override // Binder call
+ public void startPreparedClient(int cookie) {
+ checkPermission(MANAGE_BIOMETRIC);
+ startCurrentClient(cookie);
+ }
+
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(MANAGE_BIOMETRIC);
- cancelAuthenticationInternal(token, opPackageName,
- callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
}
@Override // Binder call
@@ -388,43 +374,25 @@
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* BiometricPrompt.
*/
- private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
- private IBiometricServiceReceiver mBiometricServiceReceiver;
-
- public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
- mBiometricServiceReceiver = receiver;
+ private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+ BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+ super(wrapperReceiver);
}
@Override
public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAcquired(deviceId, acquiredInfo,
- FingerprintManager.getAcquiredString(
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAcquired(acquiredInfo, FingerprintManager.getAcquiredString(
getContext(), acquiredInfo, vendorCode));
}
}
@Override
- public void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
- }
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
- }
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onError(deviceId, error,
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onError(cookie, error,
FingerprintManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -435,7 +403,6 @@
* the FingerprintManager.
*/
private class ServiceListenerImpl implements ServiceListener {
-
private IFingerprintServiceReceiver mFingerprintServiceReceiver;
public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
@@ -483,7 +450,8 @@
}
@Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
if (mFingerprintServiceReceiver != null) {
mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
}
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/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index c3e3842..bf95210 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -72,6 +72,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Protocol;
+import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
@@ -99,7 +100,7 @@
private static final String TAG = NetworkMonitor.class.getSimpleName();
private static final boolean DBG = true;
private static final boolean VDBG = false;
-
+ private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
// Default configuration values for captive portal detection probes.
// TODO: append a random length parameter to the default HTTPS url.
// TODO: randomize browser version ids in the default User-Agent String.
@@ -116,6 +117,15 @@
private static final int SOCKET_TIMEOUT_MS = 10000;
private static final int PROBE_TIMEOUT_MS = 3000;
+ // Default configuration values for data stall detection.
+ private static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
+ private static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
+ private static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000;
+
+ private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+ private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
+ (1 << DATA_STALL_EVALUATION_TYPE_DNS);
+
static enum EvaluationResult {
VALIDATED(true),
CAPTIVE_PORTAL(false);
@@ -233,6 +243,12 @@
*/
public static final int CMD_PROBE_COMPLETE = BASE + 16;
+ /**
+ * ConnectivityService notifies NetworkMonitor of DNS query responses event.
+ * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
+ */
+ public static final int EVENT_DNS_NOTIFICATION = BASE + 17;
+
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -297,6 +313,7 @@
private final State mCaptivePortalState = new CaptivePortalState();
private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
private final State mProbingState = new ProbingState();
+ private final State mWaitingForNextProbeState = new WaitingForNextProbeState();
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -314,6 +331,12 @@
private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
private int mEvaluateAttempts = 0;
private volatile int mProbeToken = 0;
+ private final int mConsecutiveDnsTimeoutThreshold;
+ private final int mDataStallMinEvaluateTime;
+ private final int mDataStallValidDnsTimeThreshold;
+ private final int mDataStallEvaluationType;
+ private final DnsStallDetector mDnsStallDetector;
+ private long mLastProbeTime;
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
NetworkRequest defaultRequest) {
@@ -346,6 +369,7 @@
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
addState(mProbingState, mEvaluatingState);
+ addState(mWaitingForNextProbeState, mEvaluatingState);
addState(mCaptivePortalState, mMaybeNotifyState);
addState(mEvaluatingPrivateDnsState, mDefaultState);
addState(mValidatedState, mDefaultState);
@@ -359,6 +383,12 @@
mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
mRandom = deps.getRandom();
+ // TODO: Evaluate to move data stall configuration to a specific class.
+ mConsecutiveDnsTimeoutThreshold = getConsecutiveDnsTimeoutThreshold();
+ mDnsStallDetector = new DnsStallDetector(mConsecutiveDnsTimeoutThreshold);
+ mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
+ mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
+ mDataStallEvaluationType = getDataStallEvalutionType();
start();
}
@@ -507,6 +537,9 @@
sendMessage(CMD_EVALUATE_PRIVATE_DNS);
break;
}
+ case EVENT_DNS_NOTIFICATION:
+ mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+ break;
default:
break;
}
@@ -537,6 +570,13 @@
case CMD_EVALUATE_PRIVATE_DNS:
transitionTo(mEvaluatingPrivateDnsState);
break;
+ case EVENT_DNS_NOTIFICATION:
+ mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+ if (isDataStall()) {
+ validationLog("Suspecting data stall, reevaluate");
+ transitionTo(mEvaluatingState);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -839,6 +879,11 @@
@Override
public void enter() {
+ if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
+ //Don't continue to blame UID forever.
+ TrafficStats.clearThreadStatsUid();
+ }
+
final int token = ++mProbeToken;
mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
isCaptivePortal())));
@@ -856,6 +901,7 @@
final CaptivePortalProbeResult probeResult =
(CaptivePortalProbeResult) message.obj;
+ mLastProbeTime = SystemClock.elapsedRealtime();
if (probeResult.isSuccessful()) {
// Transit EvaluatingPrivateDnsState to get to Validated
// state (even if no Private DNS validation required).
@@ -865,28 +911,16 @@
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
} else {
- final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
- sendMessageDelayed(msg, mReevaluateDelayMs);
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
notifyNetworkTestResultInvalid(probeResult.redirectUrl);
- if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
- // Don't continue to blame UID forever.
- TrafficStats.clearThreadStatsUid();
- }
- mReevaluateDelayMs *= 2;
- if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
- mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
- }
+ transitionTo(mWaitingForNextProbeState);
}
return HANDLED;
- case CMD_REEVALUATE:
- // Leave the event to EvaluatingState. Defer this message will result in reset
- // of mReevaluateDelayMs and mEvaluateAttempts.
- case CMD_NETWORK_DISCONNECTED:
+ case EVENT_DNS_NOTIFICATION:
+ // Leave the event to DefaultState to record correct dns timestamp.
return NOT_HANDLED;
default:
- // TODO: Some events may able to handle in this state, instead of deferring to
- // next state.
+ // Wait for probe result and defer events to next state by default.
deferMessage(message);
return HANDLED;
}
@@ -901,6 +935,29 @@
}
}
+ // Being in the WaitingForNextProbeState indicates that evaluating probes failed and state is
+ // transited from ProbingState. This ensures that the state machine is only in ProbingState
+ // while a probe is in progress, not while waiting to perform the next probe. That allows
+ // ProbingState to defer most messages until the probe is complete, which keeps the code simple
+ // and matches the pre-Q behaviour where probes were a blocking operation performed on the state
+ // machine thread.
+ private class WaitingForNextProbeState extends State {
+ @Override
+ public void enter() {
+ final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
+ sendMessageDelayed(msg, mReevaluateDelayMs);
+ mReevaluateDelayMs *= 2;
+ if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
+ mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ return NOT_HANDLED;
+ }
+ }
+
// Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
// most one per address family. This ensures we only wait up to 20 seconds for TCP connections
// to complete, regardless of how many IP addresses a host has.
@@ -947,6 +1004,29 @@
Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
}
+ private int getConsecutiveDnsTimeoutThreshold() {
+ return mDependencies.getSetting(mContext,
+ Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+ DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD);
+ }
+
+ private int getDataStallMinEvaluateTime() {
+ return mDependencies.getSetting(mContext,
+ Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+ DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS);
+ }
+
+ private int getDataStallValidDnsTimeThreshold() {
+ return mDependencies.getSetting(mContext,
+ Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
+ DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS);
+ }
+
+ private int getDataStallEvalutionType() {
+ return mDependencies.getSetting(mContext, Settings.Global.DATA_STALL_EVALUATION_TYPE,
+ DEFAULT_DATA_STALL_EVALUATION_TYPES);
+ }
+
// Static for direct access by ConnectivityService
public static String getCaptivePortalServerHttpUrl(Context context) {
return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
@@ -1462,4 +1542,127 @@
public static final Dependencies DEFAULT = new Dependencies();
}
+
+ /**
+ * Methods in this class perform no locking because all accesses are performed on the state
+ * machine's thread. Need to consider the thread safety if it ever could be accessed outside the
+ * state machine.
+ */
+ @VisibleForTesting
+ protected class DnsStallDetector {
+ private static final int DEFAULT_DNS_LOG_SIZE = 50;
+ private int mConsecutiveTimeoutCount = 0;
+ private int mSize;
+ final DnsResult[] mDnsEvents;
+ final RingBufferIndices mResultIndices;
+
+ DnsStallDetector(int size) {
+ mSize = Math.max(DEFAULT_DNS_LOG_SIZE, size);
+ mDnsEvents = new DnsResult[mSize];
+ mResultIndices = new RingBufferIndices(mSize);
+ }
+
+ @VisibleForTesting
+ protected void accumulateConsecutiveDnsTimeoutCount(int code) {
+ final DnsResult result = new DnsResult(code);
+ mDnsEvents[mResultIndices.add()] = result;
+ if (result.isTimeout()) {
+ mConsecutiveTimeoutCount++;
+ } else {
+ // Keep the event in mDnsEvents without clearing it so that there are logs to do the
+ // simulation and analysis.
+ mConsecutiveTimeoutCount = 0;
+ }
+ }
+
+ private boolean isDataStallSuspected(int timeoutCountThreshold, int validTime) {
+ if (timeoutCountThreshold <= 0) {
+ Log.wtf(TAG, "Timeout count threshold should be larger than 0.");
+ return false;
+ }
+
+ // Check if the consecutive timeout count reach the threshold or not.
+ if (mConsecutiveTimeoutCount < timeoutCountThreshold) {
+ return false;
+ }
+
+ // Check if the target dns event index is valid or not.
+ final int firstConsecutiveTimeoutIndex =
+ mResultIndices.indexOf(mResultIndices.size() - timeoutCountThreshold);
+
+ // If the dns timeout events happened long time ago, the events are meaningless for
+ // data stall evaluation. Thus, check if the first consecutive timeout dns event
+ // considered in the evaluation happened in defined threshold time.
+ final long now = SystemClock.elapsedRealtime();
+ final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp;
+ return (firstTimeoutTime < validTime);
+ }
+
+ int getConsecutiveTimeoutCount() {
+ return mConsecutiveTimeoutCount;
+ }
+ }
+
+ private static class DnsResult {
+ // TODO: Need to move the DNS return code definition to a specific class once unify DNS
+ // response code is done.
+ private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
+ private final long mTimeStamp;
+ private final int mReturnCode;
+
+ DnsResult(int code) {
+ mTimeStamp = SystemClock.elapsedRealtime();
+ mReturnCode = code;
+ }
+
+ private boolean isTimeout() {
+ return mReturnCode == RETURN_CODE_DNS_TIMEOUT;
+ }
+ }
+
+
+ @VisibleForTesting
+ protected DnsStallDetector getDnsStallDetector() {
+ return mDnsStallDetector;
+ }
+
+ private boolean dataStallEvaluateTypeEnabled(int type) {
+ return (mDataStallEvaluationType & (1 << type)) != 0;
+ }
+
+ @VisibleForTesting
+ protected long getLastProbeTime() {
+ return mLastProbeTime;
+ }
+
+ @VisibleForTesting
+ protected boolean isDataStall() {
+ boolean result = false;
+ // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
+ // possible traffic cost in metered network.
+ if (mNetworkAgentInfo.networkCapabilities.isMetered()
+ && (SystemClock.elapsedRealtime() - getLastProbeTime()
+ < mDataStallMinEvaluateTime)) {
+ return false;
+ }
+
+ // Check dns signal. Suspect it may be a data stall if both :
+ // 1. The number of consecutive DNS query timeouts > mConsecutiveDnsTimeoutThreshold.
+ // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms.
+ if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) {
+ if (mDnsStallDetector.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
+ mDataStallValidDnsTimeThreshold)) {
+ result = true;
+ logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
+ }
+ }
+
+ if (VDBG_STALL) {
+ log("isDataStall: result=" + result + ", consecutive dns timeout count="
+ + mDnsStallDetector.getConsecutiveTimeoutCount());
+ }
+
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 94c94a5..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -27,10 +27,7 @@
import static android.os.Process.SYSTEM_UID;
import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -42,7 +39,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,15 +60,14 @@
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
private static final boolean DBG = true;
- private static final Boolean SYSTEM = Boolean.TRUE;
- private static final Boolean NETWORK = Boolean.FALSE;
+ protected static final Boolean SYSTEM = Boolean.TRUE;
+ protected static final Boolean NETWORK = Boolean.FALSE;
private static final int VERSION_Q = Build.VERSION_CODES.Q;
private final Context mContext;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final INetworkManagementService mNetd;
- private final BroadcastReceiver mIntentReceiver;
// Values are User IDs.
private final Set<Integer> mUsers = new HashSet<>();
@@ -85,26 +80,6 @@
mPackageManager = context.getPackageManager();
mUserManager = UserManager.get(context);
mNetd = netd;
- mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
- Uri appData = intent.getData();
- String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
- if (Intent.ACTION_USER_ADDED.equals(action)) {
- onUserAdded(user);
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(user);
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- onAppAdded(appName, appUid);
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- onAppRemoved(appUid);
- }
- }
- };
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -112,17 +87,6 @@
public synchronized void startMonitoring() {
log("Monitoring");
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_ADDED);
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
if (apps == null) {
loge("No apps");
@@ -260,7 +224,14 @@
}
}
- private synchronized void onUserAdded(int user) {
+ /**
+ * Called when a user is added. See {link #ACTION_USER_ADDED}.
+ *
+ * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+ *
+ * @hide
+ */
+ public synchronized void onUserAdded(int user) {
if (user < 0) {
loge("Invalid user in onUserAdded: " + user);
return;
@@ -272,7 +243,14 @@
update(users, mApps, true);
}
- private synchronized void onUserRemoved(int user) {
+ /**
+ * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+ *
+ * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+ *
+ * @hide
+ */
+ public synchronized void onUserRemoved(int user) {
if (user < 0) {
loge("Invalid user in onUserRemoved: " + user);
return;
@@ -284,8 +262,8 @@
update(users, mApps, false);
}
-
- private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+ @VisibleForTesting
+ protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
if (currentPermission == SYSTEM) {
return currentPermission;
}
@@ -303,33 +281,39 @@
return currentPermission;
}
- private synchronized void onAppAdded(String appName, int appUid) {
- if (TextUtils.isEmpty(appName) || appUid < 0) {
- loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
- return;
- }
-
+ /**
+ * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+ *
+ * @param packageName The name of the new package.
+ * @param uid The uid of the new package.
+ *
+ * @hide
+ */
+ public synchronized void onPackageAdded(String packageName, int uid) {
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
- if (permission != mApps.get(appUid)) {
- mApps.put(appUid, permission);
+ final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+ if (permission != mApps.get(uid)) {
+ mApps.put(uid, permission);
Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(appUid, permission);
+ apps.put(uid, permission);
update(mUsers, apps, true);
}
}
- private synchronized void onAppRemoved(int appUid) {
- if (appUid < 0) {
- loge("Invalid app in onAppRemoved: " + appUid);
- return;
- }
+ /**
+ * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+ *
+ * @param uid containing the integer uid previously assigned to the package.
+ *
+ * @hide
+ */
+ public synchronized void onPackageRemoved(int uid) {
Map<Integer, Boolean> apps = new HashMap<>();
Boolean permission = null;
- String[] packages = mPackageManager.getPackagesForUid(appUid);
+ String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
permission = highestPermissionForUid(permission, name);
@@ -341,16 +325,16 @@
}
}
}
- if (permission == mApps.get(appUid)) {
+ if (permission == mApps.get(uid)) {
// The permissions of this UID have not changed. Nothing to do.
return;
} else if (permission != null) {
- mApps.put(appUid, permission);
- apps.put(appUid, permission);
+ mApps.put(uid, permission);
+ apps.put(uid, permission);
update(mUsers, apps, true);
} else {
- mApps.remove(appUid);
- apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
+ mApps.remove(uid);
+ apps.put(uid, NETWORK); // doesn't matter which permission we pick here
update(mUsers, apps, false);
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a8f7259..3c14393 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -23,24 +23,18 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
-import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
-import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
-import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
-import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_INVALID;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -50,6 +44,7 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+
import static com.android.server.ConnectivityService.SHORT_ARG;
import android.app.Notification;
@@ -60,7 +55,6 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -68,7 +62,6 @@
import android.hardware.usb.UsbManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -76,7 +69,7 @@
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkUtils;
-import android.net.RouteInfo;
+import android.net.ip.IpServer;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
@@ -89,15 +82,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -113,6 +103,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.LocalServices;
+import com.android.server.connectivity.tethering.EntitlementManager;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
import com.android.server.connectivity.tethering.TetheringConfiguration;
@@ -123,8 +114,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -145,18 +134,12 @@
private final static boolean DBG = false;
private final static boolean VDBG = false;
- protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
-
private static final Class[] messageClasses = {
Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
- // {@link ComponentName} of the Service used to run tether provisioning.
- private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
- .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
-
private static class TetherState {
public final IpServer ipServer;
public int lastState;
@@ -191,7 +174,6 @@
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
- private final MockableSystemProperties mSystemProperties;
private final StateMachine mTetherMasterSM;
private final OffloadController mOffloadController;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@@ -200,6 +182,7 @@
private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
+ private final EntitlementManager mEntitlementMgr;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -220,7 +203,6 @@
mStatsService = statsService;
mPolicyManager = policyManager;
mLooper = looper;
- mSystemProperties = systemProperties;
mDeps = deps;
mPublicSync = new Object();
@@ -241,12 +223,13 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+ mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
mCarrierConfigChange = new VersionedBroadcastListener(
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
mLog.log("OBSERVED carrier config change");
updateConfiguration();
- reevaluateSimCardProvisioning();
+ mEntitlementMgr.reevaluateSimCardProvisioning();
});
mStateReceiver = new StateReceiver();
@@ -289,6 +272,7 @@
private void updateConfiguration() {
mConfig = new TetheringConfiguration(mContext, mLog);
mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
+ mEntitlementMgr.updateConfiguration(mConfig);
}
private void maybeUpdateConfiguration() {
@@ -354,83 +338,54 @@
}
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
- if (!isTetherProvisioningRequired()) {
+ mEntitlementMgr.startTethering(type);
+ if (!mEntitlementMgr.isTetherProvisioningRequired()) {
enableTetheringInternal(type, true, receiver);
return;
}
+ final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
if (showProvisioningUi) {
- runUiTetherProvisioningAndEnable(type, receiver);
+ mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver);
} else {
- runSilentTetherProvisioningAndEnable(type, receiver);
+ mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver);
}
}
public void stopTethering(int type) {
enableTetheringInternal(type, false, null);
- if (isTetherProvisioningRequired()) {
- cancelTetherProvisioningRechecks(type);
+ mEntitlementMgr.stopTethering(type);
+ if (mEntitlementMgr.isTetherProvisioningRequired()) {
+ // There are lurking bugs where the notion of "provisioning required" or
+ // "tethering supported" may change without notifying tethering properly, then
+ // tethering can't shutdown correctly.
+ // TODO: cancel re-check all the time
+ if (mDeps.isTetheringSupported()) {
+ mEntitlementMgr.cancelTetherProvisioningRechecks(type);
+ }
}
}
/**
- * Check if the device requires a provisioning check in order to enable tethering.
- *
- * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
- */
- @VisibleForTesting
- protected boolean isTetherProvisioningRequired() {
- final TetheringConfiguration cfg = mConfig;
- if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || cfg.provisioningApp.length == 0) {
- return false;
- }
- if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
- return false;
- }
- return (cfg.provisioningApp.length == 2);
- }
-
- // The logic here is aimed solely at confirming that a CarrierConfig exists
- // and affirms that entitlement checks are not required.
- //
- // TODO: find a better way to express this, or alter the checking process
- // entirely so that this is more intuitive.
- private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
- // Check carrier config for entitlement checks
- final CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) return false;
-
- final PersistableBundle carrierConfig = configManager.getConfig();
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- return !isEntitlementCheckRequired;
- }
-
- /**
* Enables or disables tethering for the given type. This should only be called once
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
* for the specified interface.
*/
private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
- boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
+ boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired();
int result;
switch (type) {
case TETHERING_WIFI:
result = setWifiTethering(enable);
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
- scheduleProvisioningRechecks(type);
+ mEntitlementMgr.scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
- scheduleProvisioningRechecks(type);
+ mEntitlementMgr.scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
@@ -489,32 +444,14 @@
? TETHER_ERROR_NO_ERROR
: TETHER_ERROR_MASTER_ERROR;
sendTetherResult(receiver, result);
- if (enable && isTetherProvisioningRequired()) {
- scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
+ if (enable && mEntitlementMgr.isTetherProvisioningRequired()) {
+ mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
}
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
}, BluetoothProfile.PAN);
}
- private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
- ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
- sendUiTetherProvisionIntent(type, proxyReceiver);
- }
-
- private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
- Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
/**
* Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
* is successful before firing back up to the wrapped receiver.
@@ -546,62 +483,6 @@
return receiverForSending;
}
- private void scheduleProvisioningRechecks(int type) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_SET_ALARM, true);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
- ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
- sendSilentTetherProvisionIntent(type, proxyReceiver);
- }
-
- private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_RUN_PROVISION, true);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void cancelTetherProvisioningRechecks(int type) {
- if (mDeps.isTetheringSupported()) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- // Used by the SIM card change observation code.
- // TODO: De-duplicate with above code, where possible.
- private void startProvisionIntent(int tetherType) {
- final Intent startProvIntent = new Intent();
- startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(TETHER_SERVICE);
- mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
- }
-
public int tether(String iface) {
return tether(iface, IpServer.STATE_TETHERED);
}
@@ -1166,30 +1047,6 @@
return false;
}
- private void reevaluateSimCardProvisioning() {
- if (!mConfig.hasMobileHotspotProvisionApp()) return;
- if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
-
- ArrayList<Integer> tethered = new ArrayList<>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState != IpServer.STATE_TETHERED) {
- continue; // Skip interfaces that aren't tethered.
- }
- String iface = mTetherStates.keyAt(i);
- int interfaceType = ifaceNameToType(iface);
- if (interfaceType != TETHERING_INVALID) {
- tethered.add(interfaceType);
- }
- }
- }
-
- for (int tetherType : tethered) {
- startProvisionIntent(tetherType);
- }
- }
-
class TetherMasterSM extends StateMachine {
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering/Local Hotspot
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
new file mode 100644
index 0000000..a4e3e1d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.connectivity.tethering;
+
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+
+import static com.android.internal.R.string.config_wifi_tether_enable;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.Binder;
+import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.MockableSystemProperties;
+
+/**
+ * This class encapsulates entitlement/provisioning mechanics
+ * provisioning check only applies to the use of the mobile network as an upstream
+ *
+ * @hide
+ */
+public class EntitlementManager {
+ private static final String TAG = EntitlementManager.class.getSimpleName();
+
+ // {@link ComponentName} of the Service used to run tether provisioning.
+ private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(config_wifi_tether_enable));
+ protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
+ // The ArraySet contains enabled downstream types, ex:
+ // {@link ConnectivityManager.TETHERING_WIFI}
+ // {@link ConnectivityManager.TETHERING_USB}
+ // {@link ConnectivityManager.TETHERING_BLUETOOTH}
+ @GuardedBy("mCurrentTethers")
+ private final ArraySet<Integer> mCurrentTethers;
+ private final Context mContext;
+ private final MockableSystemProperties mSystemProperties;
+ private final SharedLog mLog;
+ @Nullable
+ private TetheringConfiguration mConfig;
+
+ public EntitlementManager(Context ctx, SharedLog log,
+ MockableSystemProperties systemProperties) {
+ mContext = ctx;
+ mLog = log;
+ mCurrentTethers = new ArraySet<Integer>();
+ mSystemProperties = systemProperties;
+ }
+
+ /**
+ * Pass a new TetheringConfiguration instance each time when
+ * Tethering#updateConfiguration() is called.
+ */
+ public void updateConfiguration(TetheringConfiguration conf) {
+ mConfig = conf;
+ }
+
+ /**
+ * Tell EntitlementManager that a given type of tethering has been enabled
+ *
+ * @param type Tethering type
+ */
+ public void startTethering(int type) {
+ synchronized (mCurrentTethers) {
+ mCurrentTethers.add(type);
+ }
+ }
+
+ /**
+ * Tell EntitlementManager that a given type of tethering has been disabled
+ *
+ * @param type Tethering type
+ */
+ public void stopTethering(int type) {
+ synchronized (mCurrentTethers) {
+ mCurrentTethers.remove(type);
+ }
+ }
+
+ /**
+ * Check if the device requires a provisioning check in order to enable tethering.
+ *
+ * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+ */
+ @VisibleForTesting
+ public boolean isTetherProvisioningRequired() {
+ if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
+ || mConfig.provisioningApp.length == 0) {
+ return false;
+ }
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
+ return false;
+ }
+ return (mConfig.provisioningApp.length == 2);
+ }
+
+ /**
+ * Re-check tethering provisioning for enabled downstream tether types.
+ * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+ */
+ public void reevaluateSimCardProvisioning() {
+ if (!mConfig.hasMobileHotspotProvisionApp()) return;
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
+
+ final ArraySet<Integer> reevaluateType;
+ synchronized (mCurrentTethers) {
+ reevaluateType = new ArraySet<Integer>(mCurrentTethers);
+ }
+ for (Integer type : reevaluateType) {
+ startProvisionIntent(type);
+ }
+ }
+
+ // The logic here is aimed solely at confirming that a CarrierConfig exists
+ // and affirms that entitlement checks are not required.
+ //
+ // TODO: find a better way to express this, or alter the checking process
+ // entirely so that this is more intuitive.
+ private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
+ // Check carrier config for entitlement checks
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) return false;
+
+ final PersistableBundle carrierConfig = configManager.getConfig();
+ if (carrierConfig == null) return false;
+
+ // A CarrierConfigManager was found and it has a config.
+ final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+ return !isEntitlementCheckRequired;
+ }
+
+ public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Used by the SIM card change observation code.
+ // TODO: De-duplicate with above code, where possible.
+ private void startProvisionIntent(int tetherType) {
+ final Intent startProvIntent = new Intent();
+ startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
+ mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+ }
+
+ public void scheduleProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_SET_ALARM, true);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void cancelTetherProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 8b40069..d56b167 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -17,19 +17,13 @@
package com.android.server.connectivity.tethering;
import android.content.Context;
-import android.net.INetd;
import android.net.NetworkRequest;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
import android.net.ip.IpServer;
-import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.os.Handler;
import android.net.util.SharedLog;
-import android.os.Looper;
+import android.os.Handler;
import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.MockableSystemProperties;
import java.util.ArrayList;
@@ -65,4 +59,9 @@
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
+
+ public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
+ MockableSystemProperties systemProperties) {
+ return new EntitlementManager(ctx, log, systemProperties);
+ }
}
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
similarity index 75%
rename from services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
rename to services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
index aac83b6..726362a 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.intelligence;
+package com.android.server.contentcapture;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -21,18 +21,18 @@
import android.os.IBinder;
/**
- * Intelligence Manager local system service interface.
+ * ContentCapture Manager local system service interface.
*
* @hide Only for use within the system server.
*/
-public abstract class IntelligenceManagerInternal {
+public abstract class ContentCaptureManagerInternal {
/**
* Checks whether the given {@code uid} owns the
- * {@link android.service.intelligence.IntelligenceService} implementation associated with the
- * given {@code userId}.
+ * {@link android.service.contentcapture.ContentCaptureService} implementation associated with
+ * the given {@code userId}.
*/
- public abstract boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId);
+ public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId);
/**
* Notifies the intelligence service of new assist data for the given activity.
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/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 33525fd..f2c539c 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -16,16 +16,7 @@
package com.android.server.display;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
import android.content.Context;
-import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
@@ -34,20 +25,29 @@
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
-import android.opengl.GLES20;
import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
import android.util.Slog;
import android.view.DisplayInfo;
-import android.view.Surface.OutOfResourcesException;
import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
-import libcore.io.Streams;
-
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
/**
* <p>
* Animates a screen transition from on to off or off to on by applying
@@ -569,37 +569,31 @@
mSurfaceSession = new SurfaceSession();
}
- SurfaceControl.openTransaction();
- try {
- if (mSurfaceControl == null) {
- try {
- int flags;
- if (mMode == MODE_FADE) {
- flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN;
- } else {
- flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN;
- }
- mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
- .setName("ColorFade")
- .setSize(mDisplayWidth, mDisplayHeight)
- .setFlags(flags)
- .build();
- } catch (OutOfResourcesException ex) {
- Slog.e(TAG, "Unable to create surface.", ex);
- return false;
+ if (mSurfaceControl == null) {
+ Transaction t = new Transaction();
+ try {
+ final SurfaceControl.Builder builder =
+ new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade");
+ if (mMode == MODE_FADE) {
+ builder.setColorLayer(true);
+ } else {
+ builder.setBufferSize(mDisplayWidth, mDisplayHeight);
}
-
- mSurfaceControl.setLayerStack(mDisplayLayerStack);
- mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
- mSurface = new Surface();
- mSurface.copyFrom(mSurfaceControl);
-
- mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
- mDisplayId, mSurfaceControl);
- mSurfaceLayout.onDisplayTransaction();
+ mSurfaceControl = builder.build();
+ } catch (OutOfResourcesException ex) {
+ Slog.e(TAG, "Unable to create surface.", ex);
+ return false;
}
- } finally {
- SurfaceControl.closeTransaction();
+
+ t.setLayerStack(mSurfaceControl, mDisplayLayerStack);
+ t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ mSurface = new Surface();
+ mSurface.copyFrom(mSurfaceControl);
+
+ mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+ mDisplayId, mSurfaceControl);
+ mSurfaceLayout.onDisplayTransaction(t);
+ t.apply();
}
return true;
}
@@ -746,7 +740,7 @@
}
@Override
- public void onDisplayTransaction() {
+ public void onDisplayTransaction(Transaction t) {
synchronized (this) {
if (mSurfaceControl == null) {
return;
@@ -755,21 +749,21 @@
DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
switch (displayInfo.rotation) {
case Surface.ROTATION_0:
- mSurfaceControl.setPosition(0, 0);
- mSurfaceControl.setMatrix(1, 0, 0, 1);
+ t.setPosition(mSurfaceControl, 0, 0);
+ t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
break;
case Surface.ROTATION_90:
- mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
- mSurfaceControl.setMatrix(0, -1, 1, 0);
+ t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight);
+ t.setMatrix(mSurfaceControl, 0, -1, 1, 0);
break;
case Surface.ROTATION_180:
- mSurfaceControl.setPosition(displayInfo.logicalWidth,
+ t.setPosition(mSurfaceControl, displayInfo.logicalWidth,
displayInfo.logicalHeight);
- mSurfaceControl.setMatrix(-1, 0, 0, -1);
+ t.setMatrix(mSurfaceControl, -1, 0, 0, -1);
break;
case Surface.ROTATION_270:
- mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
- mSurfaceControl.setMatrix(0, 1, -1, 0);
+ t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0);
+ t.setMatrix(mSurfaceControl, 0, 1, -1, 0);
break;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 7bfe9ce..6ee5665 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -225,6 +225,8 @@
viewport.deviceHeight = isRotated ? info.width : info.height;
viewport.uniqueId = info.uniqueId;
+ // TODO(b/112898898) Use an actual port here.
+ viewport.physicalPort = null;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e70460a..360a7d1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -247,9 +247,6 @@
// device).
private Point mStableDisplaySize = new Point();
- // Whether the system has finished booting or not.
- private boolean mSystemReady;
-
// The top inset of the default display.
// This gets persisted so that the boot animation knows how to transition from the display's
// full size to the size configured by the user. Right now we only persist and animate the top
@@ -322,8 +319,6 @@
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
mCurrentUserId = UserHandle.USER_SYSTEM;
-
- mSystemReady = false;
}
public void setupSchedulerPolicies() {
@@ -413,10 +408,6 @@
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
- mSystemReady = true;
- // Just in case the top inset changed before the system was ready. At this point, any
- // relevant configuration should be in place.
- recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
}
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -506,7 +497,7 @@
// List is self-synchronized copy-on-write.
for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
- listener.onDisplayTransaction();
+ listener.onDisplayTransaction(t);
}
}
@@ -1065,10 +1056,7 @@
}
private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
- // We must only persist the inset after boot has completed, otherwise we will end up
- // overwriting the persisted value before the masking flag has been loaded from the
- // resource overlay.
- if (!mSystemReady || d == null) {
+ if (d == null) {
return;
}
int topInset = d.getInsets().top;
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index d6931e0..5ca1755 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;
@@ -27,8 +26,10 @@
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 +60,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 +68,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_WIDE_COLOR = 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 +270,29 @@
}
/**
+ * Returns whether the screen is wide color gamut via SurfaceFlinger's
+ * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR}.
+ */
+ 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_WIDE_COLOR, data, reply, 0);
+ return reply.readBoolean();
+ } catch (RemoteException ex) {
+ Log.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) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6f726e6..9566598 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -57,8 +57,6 @@
* </p>
*/
final class LogicalDisplay {
- private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";
-
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
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/infra/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
new file mode 100644
index 0000000..513a6a3
--- /dev/null
+++ b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Base class representing a remote service that can queue multiple pending requests while not
+ * bound.
+ *
+ * @param <S> the concrete remote service class
+ *
+ * @hide
+ */
+public abstract class AbstractMultiplePendingRequestsRemoteService<
+ S extends AbstractMultiplePendingRequestsRemoteService<S>>
+ extends AbstractRemoteService<S> {
+
+ private final int mInitialCapacity;
+
+ protected ArrayList<PendingRequest<S>> mPendingRequests;
+
+ public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
+ @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
+ @NonNull VultureCallback callback, boolean bindInstantServiceAllowed, boolean verbose,
+ int initialCapacity) {
+ super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+ verbose);
+ mInitialCapacity = initialCapacity;
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequests() {
+ if (mPendingRequests != null) {
+ final int size = mPendingRequests.size();
+ if (mVerbose) Slog.v(mTag, "Sending " + size + " pending requests");
+ for (int i = 0; i < size; i++) {
+ mPendingRequests.get(i).run();
+ }
+ mPendingRequests = null;
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnDestroy() {
+ if (mPendingRequests != null) {
+ final int size = mPendingRequests.size();
+ if (mVerbose) Slog.v(mTag, "Canceling " + size + " pending requests");
+ for (int i = 0; i < size; i++) {
+ mPendingRequests.get(i).cancel();
+ }
+ mPendingRequests = null;
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+
+ pw.append(prefix).append("initialCapacity=").append(String.valueOf(mInitialCapacity))
+ .println();
+ final int size = mPendingRequests == null ? 0 : mPendingRequests.size();
+ pw.append(prefix).append("pendingRequests=").append(String.valueOf(size)).println();
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ if (mPendingRequests == null) {
+ mPendingRequests = new ArrayList<>(mInitialCapacity);
+ }
+ mPendingRequests.add(pendingRequest);
+ if (mVerbose) {
+ Slog.v(mTag, "queued " + mPendingRequests.size() + " requests; last=" + pendingRequest);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
similarity index 81%
rename from services/core/java/com/android/server/AbstractPerUserSystemService.java
rename to services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index b37888f..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;
}
@@ -108,7 +114,8 @@
* <p>Typically called when the service {@link Settings} property or {@link UserManager}
* restriction changed, which includes the initial creation of the service.
*
- * <p>Subclasses can extend this method to provide extra initialization.
+ * <p>Subclasses can extend this method to provide extra initialization, like clearing up
+ * previous state.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
*
@@ -129,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);
@@ -163,6 +170,20 @@
}
/**
+ * Gets the user associated with this service.
+ */
+ public final @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * Gets the master service.
+ */
+ public final M getMaster() {
+ return mMaster;
+ }
+
+ /**
* Gets this UID of the remote service this service binds to, or {@code -1} if the service is
* disabled.
*/
@@ -176,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();
}
/**
@@ -275,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 80%
rename from services/core/java/com/android/server/AbstractRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractRemoteService.java
index 181d7fd..67b3ecf 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;
@@ -45,13 +46,20 @@
*
* <p>All state of this class is modified on a handler thread.
*
+ * <p><b>NOTE: </b>this class should not be extended directly, you should extend either
+ * {@link AbstractSinglePendingRequestRemoteService} or
+ * {@link AbstractMultiplePendingRequestsRemoteService}.
+ *
* <p>See {@code com.android.server.autofill.RemoteFillService} for a concrete
* (no pun intended) example of how to use it.
*
+ * @param <S> the concrete remote service class
+ *
* @hide
*/
//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
-public abstract class AbstractRemoteService implements DeathRecipient {
+public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
+ implements DeathRecipient {
private static final int MSG_UNBIND = 1;
@@ -64,8 +72,6 @@
protected final Handler mHandler;
protected final ComponentName mComponentName;
- protected PendingRequest<? extends AbstractRemoteService> mPendingRequest;
-
private final Context mContext;
private final Intent mIntent;
private final VultureCallback mVultureCallback;
@@ -88,10 +94,11 @@
*
* @param service service that died!
*/
- void onServiceDied(AbstractRemoteService service);
+ void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service);
}
- public AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
+ // 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,
boolean bindInstantServiceAllowed, boolean verbose) {
mContext = context;
@@ -118,12 +125,25 @@
return mDestroyed;
}
+ private void handleOnConnectedStateChangedInternal(boolean connected) {
+ if (connected) {
+ handlePendingRequests();
+ }
+ handleOnConnectedStateChanged(connected);
+ }
+
/**
- * Callback called when the system connected / disconnected to the service.
+ * Handles the pending requests when the connection it bounds to the remote service.
+ */
+ abstract void handlePendingRequests();
+
+ /**
+ * Callback called when the system connected / disconnected to the service and the pending
+ * requests have been handled.
*
* @param state {@code true} when connected, {@code false} when disconnected.
*/
- protected void onConnectedStateChanged(boolean state) {
+ protected void handleOnConnectedStateChanged(boolean state) {
}
/**
@@ -144,14 +164,18 @@
private void handleDestroy() {
if (checkIfDestroyed()) return;
- if (mPendingRequest != null) {
- mPendingRequest.cancel();
- mPendingRequest = null;
- }
- ensureUnbound();
+ handleOnDestroy();
+ handleEnsureUnbound();
mDestroyed = true;
}
+ /**
+ * Clears the state when this object is destroyed.
+ *
+ * <p>Typically used to cancel the pending requests.
+ */
+ protected abstract void handleOnDestroy();
+
@Override // from DeathRecipient
public void binderDied() {
mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleBinderDied, this));
@@ -183,9 +207,7 @@
pw.append(prefix).append(tab).append("destroyed=")
.append(String.valueOf(mDestroyed)).println();
pw.append(prefix).append(tab).append("bound=")
- .append(String.valueOf(isBound())).println();
- pw.append(prefix).append(tab).append("hasPendingRequest=")
- .append(String.valueOf(mPendingRequest != null)).println();
+ .append(String.valueOf(handleIsBound())).println();
pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
pw.append(prefix).append("idleTimeout=")
.append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
@@ -194,7 +216,7 @@
pw.println();
}
- protected void scheduleRequest(PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+ protected void scheduleRequest(@NonNull PendingRequest<S> pendingRequest) {
mHandler.sendMessage(obtainMessage(
AbstractRemoteService::handlePendingRequest, this, pendingRequest));
}
@@ -205,6 +227,9 @@
protected void scheduleUnbind() {
cancelScheduledUnbind();
+ // TODO(b/111276913): implement "permanent binding"
+ // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
+ // it's not)
mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
.setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
}
@@ -212,19 +237,20 @@
private void handleUnbind() {
if (checkIfDestroyed()) return;
- ensureUnbound();
+ handleEnsureUnbound();
}
- private void handlePendingRequest(
- PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+ /**
+ * 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) {
if (checkIfDestroyed() || mCompleted) return;
- if (!isBound()) {
- if (mPendingRequest != null) {
- mPendingRequest.cancel();
- }
- mPendingRequest = pendingRequest;
- ensureBound();
+ if (!handleIsBound()) {
+ if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing " + pendingRequest);
+ handlePendingRequestWhileUnBound(pendingRequest);
+ handleEnsureBound();
} else {
if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
pendingRequest.run();
@@ -234,12 +260,17 @@
}
}
- private boolean isBound() {
+ /**
+ * Defines what to do with a request that arrives while not bound to the service.
+ */
+ abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest);
+
+ private boolean handleIsBound() {
return mServiceInterface != null;
}
- private void ensureBound() {
- if (isBound() || mBinding) return;
+ private void handleEnsureBound() {
+ if (handleIsBound() || mBinding) return;
if (mVerbose) Slog.v(mTag, "ensureBound()");
mBinding = true;
@@ -250,7 +281,7 @@
}
final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
- new UserHandle(mUserId));
+ mHandler, new UserHandle(mUserId));
if (!willBind) {
Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
@@ -262,13 +293,13 @@
}
}
- private void ensureUnbound() {
- if (!isBound() && !mBinding) return;
+ private void handleEnsureUnbound() {
+ if (!handleIsBound() && !mBinding) return;
if (mVerbose) Slog.v(mTag, "ensureUnbound()");
mBinding = false;
- if (isBound()) {
- onConnectedStateChanged(false);
+ if (handleIsBound()) {
+ handleOnConnectedStateChangedInternal(false);
if (mServiceInterface != null) {
mServiceInterface.asBinder().unlinkToDeath(this, 0);
mServiceInterface = null;
@@ -280,6 +311,7 @@
private class RemoteServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mVerbose) Slog.v(mTag, "onServiceConnected()");
if (mDestroyed || !mBinding) {
// This is abnormal. Unbinding the connection has been requested already.
Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService.");
@@ -293,15 +325,7 @@
handleBinderDied();
return;
}
- onConnectedStateChanged(true);
-
- if (mPendingRequest != null) {
- final PendingRequest<? extends AbstractRemoteService> pendingRequest =
- mPendingRequest;
- mPendingRequest = null;
- handlePendingRequest(pendingRequest);
- }
-
+ handleOnConnectedStateChangedInternal(true);
mServiceDied = false;
}
@@ -322,25 +346,12 @@
return mDestroyed;
}
- protected boolean handleResponseCallbackCommon(
- PendingRequest<? extends AbstractRemoteService> pendingRequest) {
- if (isDestroyed()) return false;
-
- if (mPendingRequest == pendingRequest) {
- mPendingRequest = null;
- }
- if (mPendingRequest == null) {
- scheduleUnbind();
- }
- return true;
- }
-
/**
* Base class for the requests serviced by the remote service.
*
* @param <S> the remote service class
*/
- public abstract static class PendingRequest<S extends AbstractRemoteService>
+ public abstract static class PendingRequest<S extends AbstractRemoteService<S>>
implements Runnable {
protected final String mTag = getClass().getSimpleName();
protected final Object mLock = new Object();
@@ -368,6 +379,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 {
diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
new file mode 100644
index 0000000..37a1f54
--- /dev/null
+++ b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+/**
+ * Base class representing a remote service that can have only one pending requests while not bound.
+ *
+ * <p>If another request is received while not bound, the previous one will be canceled.
+ *
+ * @param <S> the concrete remote service class
+ *
+ * @hide
+ */
+public abstract class AbstractSinglePendingRequestRemoteService<
+ S extends AbstractSinglePendingRequestRemoteService<S>> extends AbstractRemoteService<S> {
+
+ protected PendingRequest<S> mPendingRequest;
+
+ public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
+ @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
+ @NonNull VultureCallback callback, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+ verbose);
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequests() {
+ if (mPendingRequest != null) {
+ final PendingRequest<S> pendingRequest = mPendingRequest;
+ mPendingRequest = null;
+ handlePendingRequest(pendingRequest);
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnDestroy() {
+ if (mPendingRequest != null) {
+ mPendingRequest.cancel();
+ mPendingRequest = null;
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+ pw.append(prefix).append("hasPendingRequest=")
+ .append(String.valueOf(mPendingRequest != null)).println();
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ if (mPendingRequest != null) {
+ if (mVerbose) {
+ Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest
+ + " to handle " + pendingRequest);
+ }
+ mPendingRequest.cancel();
+ }
+ mPendingRequest = pendingRequest;
+ }
+}
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/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java
new file mode 100644
index 0000000..970e86a
--- /dev/null
+++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.input;
+
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+
+class ConfigurationProcessor {
+ private static final String TAG = "ConfigurationProcessor";
+
+ static List<String> processExcludedDeviceNames(InputStream xml) throws Exception {
+ List<String> names = new ArrayList<>();
+ try (InputStreamReader confReader = new InputStreamReader(xml)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(confReader);
+ XmlUtils.beginDocument(parser, "devices");
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ names.add(name);
+ }
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Parse the configuration for input port associations.
+ *
+ * Configuration format:
+ * <code>
+ * <ports>
+ * <port display="0" input="usb-xhci-hcd.0.auto-1.4.3/input0" />
+ * <port display="1" input="usb-xhci-hcd.0.auto-1.4.2/input0" />
+ * </ports>
+ * </code>
+ *
+ * In this example, any input device that has physical port of
+ * "usb-xhci-hcd.0.auto-1.4.3/input0" will be associated with a display
+ * that has the physical port "0". If such a display does not exist, the input device
+ * will be disabled and no input events will be dispatched from that input device until a
+ * matching display appears. Likewise, an input device that has port "..1.4.2.." will have
+ * its input events forwarded to a display that has physical port of "1".
+ *
+ * Note: display port must be a numeric value, and this is checked at runtime for validity.
+ * At the same time, it is specified as a string for simplicity.
+ *
+ * Note: do not confuse "display id" with "display port".
+ * The "display port" is the physical port on which the display is connected. This could
+ * be something like HDMI0, HDMI1, etc. For virtual displays, "display port" will be null.
+ * The "display id" is a way to identify a particular display, and is not a stable API.
+ * All displays, including virtual ones, will have a display id.
+ *
+ * Return the pairs of associations. The first item in the pair is the input port,
+ * the second item in the pair is the display port.
+ */
+ @VisibleForTesting
+ static List<Pair<String, String>> processInputPortAssociations(InputStream xml)
+ throws Exception {
+ List<Pair<String, String>> associations = new ArrayList<>();
+ try (InputStreamReader confReader = new InputStreamReader(xml)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(confReader);
+ XmlUtils.beginDocument(parser, "ports");
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ String entryName = parser.getName();
+ if (!"port".equals(entryName)) {
+ break;
+ }
+ String inputPort = parser.getAttributeValue(null, "input");
+ String displayPort = parser.getAttributeValue(null, "display");
+ if (TextUtils.isEmpty(inputPort) || TextUtils.isEmpty(displayPort)) {
+ // This is likely an error by an OEM during device configuration
+ Slog.wtf(TAG, "Ignoring incomplete entry");
+ continue;
+ }
+ try {
+ Integer.parseUnsignedInt(displayPort);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Display port should be an integer");
+ continue;
+ }
+ associations.add(new Pair<>(inputPort, displayPort));
+ }
+ }
+ return associations;
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3a31c9c..d96b6cb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -64,18 +64,18 @@
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.Xml;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IWindow;
-import android.view.InputChannel;
import android.view.InputApplicationHandle;
-import android.view.InputWindowHandle;
+import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputWindowHandle;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -97,14 +97,13 @@
import libcore.io.IoUtils;
import libcore.io.Streams;
-import org.xmlpull.v1.XmlPullParser;
-
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -124,6 +123,7 @@
static final boolean DEBUG = false;
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+ private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
@@ -1443,25 +1443,10 @@
}
}
- public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
- nativeSetInputWindows(mPtr, windowHandles, displayId);
- }
-
public void setFocusedApplication(int displayId, InputApplicationHandle application) {
nativeSetFocusedApplication(mPtr, displayId, application);
}
- public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
- final IWindow newFocusedWindow =
- focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
- if (mFocusedWindow != newFocusedWindow) {
- if (mFocusedWindowHasCapture) {
- setPointerCapture(false);
- }
- mFocusedWindow = newFocusedWindow;
- }
- }
-
public void setFocusedDisplay(int displayId) {
nativeSetFocusedDisplay(mPtr, displayId);
}
@@ -1799,11 +1784,22 @@
mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
+ // Native callback
+ private void notifyFocusChanged(IBinder token) {
+ if (mFocusedWindow != token) {
+ if (mFocusedWindowHasCapture) {
+ setPointerCapture(false);
+ }
+ if (token instanceof IWindow) {
+ mFocusedWindow = (IWindow) token;
+ }
+ }
+ }
+
// Native callback.
- private long notifyANR(InputApplicationHandle inputApplicationHandle,
- IBinder token, String reason) {
+ private long notifyANR(IBinder token, String reason) {
return mWindowManagerCallbacks.notifyANR(
- inputApplicationHandle, token, reason);
+ token, reason);
}
// Native callback.
@@ -1834,14 +1830,12 @@
}
// Native callback.
- private long interceptKeyBeforeDispatching(IBinder focus,
- KeyEvent event, int policyFlags) {
+ private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
// Native callback.
- private KeyEvent dispatchUnhandledKey(IBinder focus,
- KeyEvent event, int policyFlags) {
+ private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
}
@@ -1858,11 +1852,9 @@
}
// Native callback.
- private String[] getExcludedDeviceNames() {
- ArrayList<String> names = new ArrayList<String>();
-
+ private static String[] getExcludedDeviceNames() {
+ List<String> names = new ArrayList<>();
// Read partner-provided list of excluded input devices
- XmlPullParser parser = null;
// Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
final File[] baseDirs = {
Environment.getRootDirectory(),
@@ -1870,33 +1862,52 @@
};
for (File baseDir: baseDirs) {
File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
- FileReader confreader = null;
try {
- confreader = new FileReader(confFile);
- parser = Xml.newPullParser();
- parser.setInput(confreader);
- XmlUtils.beginDocument(parser, "devices");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (!"device".equals(parser.getName())) {
- break;
- }
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- names.add(name);
- }
- }
+ InputStream stream = new FileInputStream(confFile);
+ names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
} catch (Exception e) {
- Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
- } finally {
- try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
}
}
+ return names.toArray(new String[0]);
+ }
- return names.toArray(new String[names.size()]);
+ /**
+ * Flatten a list of pairs into a list, with value positioned directly next to the key
+ * @return Flattened list
+ */
+ private static <T> List<T> flatten(@NonNull List<Pair<T, T>> pairs) {
+ List<T> list = new ArrayList<>(pairs.size() * 2);
+ for (Pair<T, T> pair : pairs) {
+ list.add(pair.first);
+ list.add(pair.second);
+ }
+ return list;
+ }
+
+ /**
+ * Ports are highly platform-specific, so only allow these to be specified in the vendor
+ * directory.
+ */
+ // Native callback
+ private static String[] getInputPortAssociations() {
+ File baseDir = Environment.getVendorDirectory();
+ File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
+
+ try {
+ InputStream stream = new FileInputStream(confFile);
+ List<Pair<String, String>> associations =
+ ConfigurationProcessor.processInputPortAssociations(stream);
+ List<String> associationList = flatten(associations);
+ return associationList.toArray(new String[0]);
+ } catch (FileNotFoundException e) {
+ // Most of the time, file will not exist, which is expected.
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
+ }
+ return new String[0];
}
// Native callback.
@@ -1993,8 +2004,7 @@
public void notifyInputChannelBroken(IBinder token);
- public long notifyANR(InputApplicationHandle inputApplicationHandle,
- IBinder token, String reason);
+ public long notifyANR(IBinder token, String reason);
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1dfb86a..a8da968 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2420,8 +2420,9 @@
.setContentText(summary)
.setContentIntent(mImeSwitchPendingIntent);
try {
+ // TODO(b/120076400): Figure out what is the best behavior
if ((mNotificationManager != null)
- && !mIWindowManager.hasNavigationBar()) {
+ && !mIWindowManager.hasNavigationBar(DEFAULT_DISPLAY)) {
if (DEBUG) {
Slog.d(TAG, "--- show notification: label = " + summary);
}
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..173f074 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -77,8 +77,6 @@
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 +95,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 +110,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 +133,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 +154,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,7 +172,7 @@
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. */
@@ -245,11 +244,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 +267,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 +291,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 +302,10 @@
}
}
- // 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();
+ }
}
}
@@ -352,6 +341,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 +379,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 +510,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 +630,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 +682,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 +724,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);
}
}
@@ -1138,6 +1250,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 +1276,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 +1340,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();
@@ -1869,6 +1986,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 +2024,9 @@
void setNextHeartbeatAlarm() {
final long heartbeatLength;
synchronized (mLock) {
+ if (!mConstants.USE_HEARTBEATS) {
+ return;
+ }
heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
}
final long now = sElapsedRealtimeClock.millis();
@@ -1976,48 +2099,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);
+ }
}
}
}
@@ -2052,7 +2178,7 @@
if (DEBUG) {
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
}
- assignJobsToContextsLocked();
+ mConcurrencyManager.assignJobsToContextsLocked();
reportActiveLocked();
}
@@ -2068,7 +2194,7 @@
return curPriority;
}
- private int evaluateJobPriorityLocked(JobStatus job) {
+ int evaluateJobPriorityLocked(JobStatus job) {
int priority = job.getPriority();
if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
return adjustJobPriority(priority, job);
@@ -2080,161 +2206,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 +2335,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 +2980,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] ...");
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..6deecbd 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,9 +1007,13 @@
* @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).
+ // 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
@@ -1169,6 +1202,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 +1241,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 +1276,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";
@@ -1367,7 +1413,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 +1422,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 +1434,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 +1597,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 +1629,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..f73ffac
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -0,0 +1,1299 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+ }
+
+ @VisibleForTesting
+ 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);
+ return 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 (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 jobs ran during this session.
+ public final int jobCount;
+
+ TimingSession(long startElapsed, long endElapsed, int jobCount) {
+ this.startTimeElapsed = startElapsed;
+ this.endTimeElapsed = endElapsed;
+ this.jobCount = jobCount;
+ }
+
+ @Override
+ public String toString() {
+ return "TimingSession{" + startTimeElapsed + "->" + endTimeElapsed + ", " + jobCount
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TimingSession) {
+ TimingSession other = (TimingSession) obj;
+ return startTimeElapsed == other.startTimeElapsed
+ && endTimeElapsed == other.endTimeElapsed
+ && jobCount == other.jobCount;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(new long[] {startTimeElapsed, endTimeElapsed, jobCount});
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.print(startTimeElapsed);
+ pw.print(" -> ");
+ pw.print(endTimeElapsed);
+ pw.print(" (");
+ pw.print(endTimeElapsed - startTimeElapsed);
+ pw.print("), ");
+ pw.print(jobCount);
+ pw.print(" 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.JOB_COUNT, jobCount);
+
+ proto.end(token);
+ }
+ }
+
+ private final class Timer {
+ private final Package mPkg;
+
+ // List of jobs currently running for this package.
+ private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
+ private long mStartTimeElapsed;
+ private int mJobCount;
+
+ Timer(int userId, String packageName) {
+ mPkg = new Package(userId, packageName);
+ }
+
+ void startTrackingJob(@NonNull JobStatus jobStatus) {
+ if (DEBUG) Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
+ synchronized (mLock) {
+ // Always track jobs, even when charging.
+ mRunningJobs.add(jobStatus);
+ if (!mChargeTracker.isCharging()) {
+ mJobCount++;
+ if (mRunningJobs.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 (mRunningJobs.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;
+ }
+ mRunningJobs.remove(jobStatus);
+ if (!mChargeTracker.isCharging() && mRunningJobs.size() == 0) {
+ emitSessionLocked(sElapsedRealtimeClock.millis());
+ cancelCutoff();
+ }
+ }
+ }
+
+ private void emitSessionLocked(long nowElapsed) {
+ if (mJobCount <= 0) {
+ // Nothing to emit.
+ return;
+ }
+ TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mJobCount);
+ saveTimingSession(mPkg.userId, mPkg.packageName, ts);
+ mJobCount = 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 mJobCount > 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 (mRunningJobs.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.
+ mJobCount = mRunningJobs.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(mJobCount);
+ pw.print(" running jobs");
+ pw.println();
+ pw.increaseIndent();
+ for (int i = 0; i < mRunningJobs.size(); i++) {
+ JobStatus js = mRunningJobs.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.JOB_COUNT, mJobCount);
+ for (int i = 0; i < mRunningJobs.size(); i++) {
+ JobStatus js = mRunningJobs.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..b439c0d 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -53,22 +53,31 @@
* 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() {
+ }
+
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/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/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9e6e381..d5e4681 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -97,8 +97,8 @@
*
* {@hide}
*/
-public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback,
- GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends LocationProviderInterface
+ implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
private static final String TAG = "GnssLocationProvider";
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..6785964 100644
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/core/java/com/android/server/location/LocationProviderInterface.java
@@ -16,33 +16,63 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+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 android.os.Bundle;
-import android.os.WorkSource;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* Location Manager's interface for location providers.
* @hide
*/
-public interface LocationProviderInterface {
- public String getName();
+public abstract class LocationProviderInterface {
- public void enable();
- public void disable();
- public boolean isEnabled();
- public void setRequest(ProviderRequest request, WorkSource source);
+ /** Get name. */
+ public abstract String getName();
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+ /** Enable. */
+ public abstract void enable();
- // --- deprecated (but still supported) ---
- public ProviderProperties getProperties();
- public int getStatus(Bundle extras);
- public long getStatusUpdateTime();
- public boolean sendExtraCommand(String command, Bundle extras);
+ /** 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 bb86b48..b408414 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -41,7 +41,7 @@
/**
* Proxy for ILocationProvider implementations.
*/
-public class LocationProviderProxy implements LocationProviderInterface {
+public class LocationProviderProxy extends LocationProviderInterface {
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 8578761..145aee3 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -25,31 +25,31 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
/**
* A mock location provider used by LocationManagerService to implement test providers.
*
* {@hide}
*/
-public class MockProvider implements LocationProviderInterface {
+public class MockProvider extends LocationProviderInterface {
private final String mName;
private final ProviderProperties mProperties;
private final ILocationManager mLocationManager;
private final Location mLocation;
- private final Bundle mExtras = new Bundle();
+
+ private boolean mHasLocation;
+ private boolean mEnabled;
+
private int mStatus;
private long mStatusUpdateTime;
- private boolean mHasLocation;
- private boolean mHasStatus;
- private boolean mEnabled;
+ private Bundle mExtras;
private static final String TAG = "MockProvider";
@@ -61,6 +61,10 @@
mLocationManager = locationManager;
mProperties = properties;
mLocation = new Location(name);
+
+ mStatus = LocationProvider.AVAILABLE;
+ mStatusUpdateTime = 0L;
+ mExtras = null;
}
@Override
@@ -90,13 +94,12 @@
@Override
public int getStatus(Bundle extras) {
- if (mHasStatus) {
+ if (mExtras != null) {
extras.clear();
extras.putAll(mExtras);
- return mStatus;
- } else {
- return LocationProvider.AVAILABLE;
}
+
+ return mStatus;
}
@Override
@@ -120,19 +123,14 @@
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.clear();
- if (extras != null) {
- mExtras.putAll(extras);
- }
- mHasStatus = true;
- }
-
- public void clearStatus() {
- mHasStatus = false;
- mStatusUpdateTime = 0;
+ mExtras = extras;
}
@Override
@@ -145,9 +143,6 @@
pw.println(prefix + "mHasLocation=" + mHasLocation);
pw.println(prefix + "mLocation:");
mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- pw.println(prefix + "mHasStatus=" + mHasStatus);
- pw.println(prefix + "mStatus=" + mStatus);
- pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
pw.println(prefix + "mExtras=" + mExtras);
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 71bae07..99c9214 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -16,22 +16,20 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
-import android.location.LocationProvider;
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;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* A passive location provider reports locations received from other providers
@@ -40,7 +38,7 @@
*
* {@hide}
*/
-public class PassiveProvider implements LocationProviderInterface {
+public class PassiveProvider extends LocationProviderInterface {
private static final String TAG = "PassiveProvider";
private static final ProviderProperties PROPERTIES = new ProviderProperties(
@@ -78,20 +76,6 @@
}
@Override
- public int getStatus(Bundle extras) {
- if (mReportLocation) {
- return LocationProvider.AVAILABLE;
- } else {
- return LocationProvider.TEMPORARILY_UNAVAILABLE;
- }
- }
-
- @Override
- public long getStatusUpdateTime() {
- return -1;
- }
-
- @Override
public void setRequest(ProviderRequest request, WorkSource source) {
mReportLocation = request.reportLocation;
}
diff --git a/services/core/java/com/android/server/location/gps_debug.conf b/services/core/java/com/android/server/location/gps_debug.conf
new file mode 100644
index 0000000..34ce96f
--- /dev/null
+++ b/services/core/java/com/android/server/location/gps_debug.conf
@@ -0,0 +1,52 @@
+# Sample file for use for on device debug override only
+# Prefer frameworks/base/core/res/res/values/config.xml and
+# frameworks/base/core/res/res/values-mcc*-mnc*/config.xml
+
+################################
+##### AGPS server settings #####
+################################
+# FOR SUPL SUPPORT, set the following
+# SUPL_HOST=supl.google.com or IP
+# SUPL_PORT=7275
+
+# supl version 2.0
+# SUPL_VER=0x20000
+
+#SUPL_MODE is a bit mask set in config.xml per carrier by default.
+#If it is uncommented here, this value will overwrite the value from
+#config.xml.
+#MSA=0X2
+#MSB=0X1
+#SUPL_MODE=1
+
+# Emergency SUPL, 1=enable, 0=disable
+#SUPL_ES=0
+
+#Choose PDN for Emergency SUPL
+#1 - Use emergency PDN
+#0 - Use regular SUPL PDN for Emergency SUPL
+#USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=0
+
+####################################
+# LTE Positioning Profile Settings
+####################################
+# 0: Enable RRLP on LTE(Default)
+# 1: Enable LPP_User_Plane on LTE
+# 2: Enable LPP_Control_Plane
+# 3: Enable both LPP_User_Plane and LPP_Control_Plane
+#LPP_PROFILE = 2
+
+##################################################
+# Select Positioning Protocol on A-GLONASS system
+##################################################
+# 0x1: RRC CPlane
+# 0x2: RRLP UPlane
+# 0x4: LLP Uplane
+#A_GLONASS_POS_PROTOCOL_SELECT = 0
+
+# Below bit mask configures how GPS functionalities
+# should be locked when user turns off GPS on Settings
+# Set bit 0x1 if MO GPS functionalities are to be locked
+# Set bit 0x2 if NI GPS functionalities are to be locked
+# default - non is locked for backward compatibility
+#GPS_LOCK = 0
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/SP800Derive.java b/services/core/java/com/android/server/locksettings/SP800Derive.java
new file mode 100644
index 0000000..77561fc
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/SP800Derive.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Implementation of NIST SP800-108
+ * "Recommendation for Key Derivation Using Pseudorandom Functions"
+ * Hardcoded:
+ * [PRF=HMAC_SHA256]
+ * [CTRLOCATION=BEFORE_FIXED]
+ * [RLEN=32_BITS]
+ * L = 256
+ * L suffix: 32 bits
+ */
+class SP800Derive {
+ private final byte[] mKeyBytes;
+
+ SP800Derive(byte[] keyBytes) {
+ mKeyBytes = keyBytes;
+ }
+
+ private Mac getMac() {
+ try {
+ final Mac m = Mac.getInstance("HmacSHA256");
+ m.init(new SecretKeySpec(mKeyBytes, m.getAlgorithm()));
+ return m;
+ } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void update32(Mac m, int v) {
+ m.update(ByteBuffer.allocate(Integer.BYTES).putInt(v).array());
+ }
+
+ /**
+ * Generate output from a single, fixed input.
+ */
+ public byte[] fixedInput(byte[] fixedInput) {
+ final Mac m = getMac();
+ update32(m, 1); // Hardwired counter value
+ m.update(fixedInput);
+ return m.doFinal();
+ }
+
+ /**
+ * Generate output from a label and context. We add a length field at the end of the context to
+ * disambiguate it from the length even in the presence of zero bytes.
+ */
+ public byte[] withContext(byte[] label, byte[] context) {
+ final Mac m = getMac();
+ // Hardwired counter value: 1
+ update32(m, 1); // Hardwired counter value
+ m.update(label);
+ m.update((byte) 0);
+ m.update(context);
+ update32(m, context.length * 8); // Disambiguate context
+ update32(m, 256); // Hardwired output length
+ return m.doFinal();
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 596daeb..d32c299 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -26,9 +26,9 @@
import android.hardware.weaver.V1_0.WeaverReadResponse;
import android.hardware.weaver.V1_0.WeaverReadStatus;
import android.hardware.weaver.V1_0.WeaverStatus;
-import android.security.GateKeeper;
import android.os.RemoteException;
import android.os.UserManager;
+import android.security.GateKeeper;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.util.ArrayMap;
@@ -102,7 +102,8 @@
private static final int INVALID_WEAVER_SLOT = -1;
private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
- private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
@@ -128,6 +129,8 @@
private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
+ private static final byte[] PERSONALISATION_CONTEXT =
+ "android-synthetic-password-personalization-context".getBytes();
static class AuthenticationResult {
public AuthenticationToken authToken;
@@ -136,6 +139,7 @@
}
static class AuthenticationToken {
+ private final byte mVersion;
/*
* Here is the relationship between all three fields:
* P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
@@ -146,29 +150,38 @@
private @Nullable byte[] P1;
private @NonNull String syntheticPassword;
+ AuthenticationToken(byte version) {
+ mVersion = version;
+ }
+
+ private byte[] derivePassword(byte[] personalization) {
+ if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+ return (new SP800Derive(syntheticPassword.getBytes()))
+ .withContext(personalization, PERSONALISATION_CONTEXT);
+ } else {
+ return SyntheticPasswordCrypto.personalisedHash(personalization,
+ syntheticPassword.getBytes());
+ }
+ }
+
public String deriveKeyStorePassword() {
- return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
- PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
+ return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
}
public byte[] deriveGkPassword() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_SP_GK_AUTH);
}
public byte[] deriveDiskEncryptionKey() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_FBE_KEY);
}
public byte[] deriveVendorAuthSecret() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
}
public byte[] derivePasswordHashFactor() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_PASSWORD_HASH);
}
private void initialize(byte[] P0, byte[] P1) {
@@ -185,7 +198,7 @@
}
protected static AuthenticationToken create() {
- AuthenticationToken result = new AuthenticationToken();
+ AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
secureRandom(SYNTHETIC_PASSWORD_LENGTH));
return result;
@@ -802,7 +815,16 @@
}
byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
byte[] blob = new byte[content.length + 1 + 1];
- blob[0] = SYNTHETIC_PASSWORD_VERSION;
+ /*
+ * We can upgrade from v1 to v2 because that's just a change in the way that
+ * the SP is stored. However, we can't upgrade to v3 because that is a change
+ * in the way that passwords are derived from the SP.
+ */
+ if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+ blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
+ } else {
+ blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
+ }
blob[1] = type;
System.arraycopy(content, 0, blob, 2, content.length);
saveState(SP_BLOB_NAME, blob, handle, userId);
@@ -940,7 +962,9 @@
return null;
}
final byte version = blob[0];
- if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
+ if (version != SYNTHETIC_PASSWORD_VERSION_V3
+ && version != SYNTHETIC_PASSWORD_VERSION_V2
+ && version != SYNTHETIC_PASSWORD_VERSION_V1) {
throw new RuntimeException("Unknown blob version");
}
if (blob[1] != type) {
@@ -958,7 +982,7 @@
Log.e(TAG, "Fail to decrypt SP for user " + userId);
return null;
}
- AuthenticationToken result = new AuthenticationToken();
+ AuthenticationToken result = new AuthenticationToken(version);
if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
if (!loadEscrowData(result, userId)) {
Log.e(TAG, "User is not escrowable: " + userId);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c18a79f..93b6620 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -923,7 +923,7 @@
@Override
public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
KeyEvent keyEvent, boolean needWakeLock) {
- if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
Log.w(TAG, "Attempted to dispatch null or non-media key event.");
return;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4f4b6bf..4bd8f45 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -64,6 +64,7 @@
private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
+ private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
static final int NTWK_BLOCKED_POWER = 0;
static final int NTWK_ALLOWED_NON_METERED = 1;
@@ -145,6 +146,13 @@
}
}
+ void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted));
+ mEventsBuffer.appIdleWlChanged(uid, isWhitelisted);
+ }
+ }
+
void paroleStateChanged(boolean paroleOn) {
synchronized (mLock) {
if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
@@ -259,6 +267,10 @@
return "App idle state of uid " + uid + ": " + idle;
}
+ private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) {
+ return "App idle whitelist state of uid " + uid + ": " + isWhitelisted;
+ }
+
private static String getParoleStateChanged(boolean paroleOn) {
return "Parole state: " + paroleOn;
}
@@ -409,6 +421,17 @@
data.timeStamp = System.currentTimeMillis();
}
+ public void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_APP_IDLE_WL_CHANGED;
+ data.ifield1 = uid;
+ data.bfield1 = isWhitelisted;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
public void paroleStateChanged(boolean paroleOn) {
final Data data = getNextSlot();
if (data == null) return;
@@ -487,6 +510,8 @@
return getDeviceIdleModeEnabled(data.bfield1);
case EVENT_APP_IDLE_STATE_CHANGED:
return getAppIdleChangedLog(data.ifield1, data.bfield1);
+ case EVENT_APP_IDLE_WL_CHANGED:
+ return getAppIdleWlChangedLog(data.ifield1, data.bfield1);
case EVENT_PAROLE_STATE_CHANGED:
return getParoleStateChanged(data.bfield1);
case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 099671d..7f650ee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -105,6 +105,12 @@
public abstract void onAdminDataAvailable();
/**
+ * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may
+ * still be in effect.
+ */
+ public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist);
+
+ /**
* Sets a list of packages which are restricted by admin from accessing metered data.
*
* @param packageNames the list of restricted packages.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d799642..0d6dadf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -464,6 +464,10 @@
@GuardedBy("mUidRulesFirstLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ // "Power save mode" is the concept used in the DeviceIdleController that includes various
+ // features including Doze and Battery Saver. It include Battery Saver, but "power save mode"
+ // and "battery saver" are not equivalent.
+
/**
* UIDs that have been white-listed to always be able to have network access
* in power save mode, except device idle (doze) still applies.
@@ -484,6 +488,13 @@
private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
/**
+ * UIDs that have been white-listed temporarily to be able to have network access despite being
+ * idle. Other power saving restrictions still apply.
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray();
+
+ /**
* UIDs that have been initially white-listed by system to avoid restricted background.
*/
@GuardedBy("mUidRulesFirstLock")
@@ -543,7 +554,7 @@
final Handler mHandler;
@VisibleForTesting
- public final Handler mUidEventHandler;
+ final Handler mUidEventHandler;
private final ServiceThread mUidEventThread;
@@ -1454,7 +1465,7 @@
}
@VisibleForTesting
- public void updateNetworks() throws InterruptedException {
+ void updateNetworks() throws InterruptedException {
updateNetworksInternal();
final CountDownLatch latch = new CountDownLatch(1);
mHandler.post(() -> {
@@ -1499,7 +1510,7 @@
* @return cycleDay to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
+ int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
int fallbackCycleDay) {
if (config == null) {
return fallbackCycleDay;
@@ -1531,7 +1542,7 @@
* @return warningBytes to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
+ long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
long fallbackWarningBytes) {
if (config == null) {
return fallbackWarningBytes;
@@ -1564,7 +1575,7 @@
* @return limitBytes to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
+ long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
long fallbackLimitBytes) {
if (config == null) {
return fallbackLimitBytes;
@@ -2028,7 +2039,7 @@
}
@VisibleForTesting
- public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
+ NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
final RecurrenceRule cycleRule = NetworkPolicy
.buildRule(ZonedDateTime.now().getDayOfMonth(), ZoneId.systemDefault());
@@ -3372,6 +3383,20 @@
fout.decreaseIndent();
}
+ size = mAppIdleTempWhitelistAppIds.size();
+ if (size > 0) {
+ fout.println("App idle whitelist app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mAppIdleTempWhitelistAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mAppIdleTempWhitelistAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
size = mDefaultRestrictBackgroundWhitelistUids.size();
if (size > 0) {
fout.println("Default restrict background whitelist uids:");
@@ -3464,7 +3489,7 @@
}
@VisibleForTesting
- public boolean isUidForeground(int uid) {
+ boolean isUidForeground(int uid) {
synchronized (mUidRulesFirstLock) {
return isUidStateForeground(
mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
@@ -3640,12 +3665,15 @@
}
/**
+ * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze
+ * mode, and app idle).
+ *
* @param deviceIdleMode if true then we don't consider
* {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
* whitelisted.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
+ private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
final int appId = UserHandle.getAppId(uid);
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
|| mPowerSaveWhitelistAppIds.get(appId);
@@ -3660,7 +3688,7 @@
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
- final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
+ final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
chain == FIREWALL_CHAIN_DOZABLE);
if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
@@ -3712,8 +3740,10 @@
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
&& !isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+ if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
} else {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -3896,7 +3926,61 @@
return UserHandle.isApp(uid) && hasInternetPermissions(uid);
}
- private boolean isUidIdle(int uid) {
+ /**
+ * Set whether or not an app should be whitelisted for network access while in app idle. Other
+ * power saving restrictions may still apply.
+ */
+ @VisibleForTesting
+ void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mUidRulesFirstLock) {
+ if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) {
+ // No change.
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLogger.appIdleWlChanged(uid, shouldWhitelist);
+ if (shouldWhitelist) {
+ mAppIdleTempWhitelistAppIds.put(uid, true);
+ } else {
+ mAppIdleTempWhitelistAppIds.delete(uid);
+ }
+ updateRuleForAppIdleUL(uid);
+ updateRulesForPowerRestrictionsUL(uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ /** Return the list of UIDs currently in the app idle whitelist. */
+ @VisibleForTesting
+ int[] getAppIdleWhitelist() {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mUidRulesFirstLock) {
+ final int len = mAppIdleTempWhitelistAppIds.size();
+ int[] uids = new int[len];
+ for (int i = 0; i < len; ++i) {
+ uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i);
+ }
+ return uids;
+ }
+ }
+
+ /** Returns if the UID is currently considered idle. */
+ @VisibleForTesting
+ boolean isUidIdle(int uid) {
+ synchronized (mUidRulesFirstLock) {
+ if (mAppIdleTempWhitelistAppIds.get(uid)) {
+ // UID is temporarily whitelisted.
+ return false;
+ }
+ }
+
final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
final int userId = UserHandle.getUserId(uid);
@@ -3940,6 +4024,7 @@
mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
mPowerSaveWhitelistAppIds.delete(uid);
mPowerSaveTempWhitelistAppIds.delete(uid);
+ mAppIdleTempWhitelistAppIds.delete(uid);
// ...then update iptables asynchronously.
mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -3984,7 +4069,7 @@
* <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
* also blacklisted.
* <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
- * no UIDs other those whitelisted will have access.
+ * no UIDs other than those whitelisted will have access.
* <ul>
*
* <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
@@ -4194,7 +4279,7 @@
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
- final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);
+ final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
int newRule = RULE_NONE;
@@ -4761,13 +4846,13 @@
}
@VisibleForTesting
- public void addIdleHandler(IdleHandler handler) {
+ void addIdleHandler(IdleHandler handler) {
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
@GuardedBy("mUidRulesFirstLock")
@VisibleForTesting
- public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
+ void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
mRestrictBackgroundPowerState = result;
boolean restrictBackground = result.batterySaverEnabled;
@@ -5023,6 +5108,11 @@
}
@Override
+ public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist);
+ }
+
+ @Override
public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) {
setMeteredRestrictedPackagesInternal(packageNames, userId);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 56d41c5..156c01d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -78,6 +78,8 @@
pw.println(" Adds a UID to the whitelist for restrict background usage.");
pw.println(" add restrict-background-blacklist UID");
pw.println(" Adds a UID to the blacklist for restrict background usage.");
+ pw.println(" add app-idle-whitelist UID");
+ pw.println(" Adds a UID to the temporary app idle whitelist.");
pw.println(" get restrict-background");
pw.println(" Gets the global restrict background usage status.");
pw.println(" list wifi-networks [true|false]");
@@ -92,6 +94,8 @@
pw.println(" Removes a UID from the whitelist for restrict background usage.");
pw.println(" remove restrict-background-blacklist UID");
pw.println(" Removes a UID from the blacklist for restrict background usage.");
+ pw.println(" remove app-idle-whitelist UID");
+ pw.println(" Removes a UID from the temporary app idle whitelist.");
pw.println(" set metered-network ID [undefined|true|false]");
pw.println(" Toggles whether the given wi-fi network is metered.");
pw.println(" set restrict-background BOOLEAN");
@@ -142,6 +146,8 @@
return -1;
}
switch(type) {
+ case "app-idle-whitelist":
+ return listAppIdleWhitelist();
case "wifi-networks":
return listWifiNetworks();
case "restrict-background-whitelist":
@@ -165,6 +171,8 @@
return addRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
return addRestrictBackgroundBlacklist();
+ case "app-idle-whitelist":
+ return addAppIdleWhitelist();
}
pw.println("Error: unknown add type '" + type + "'");
return -1;
@@ -182,14 +190,20 @@
return removeRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
return removeRestrictBackgroundBlacklist();
+ case "app-idle-whitelist":
+ return removeAppIdleWhitelist();
}
pw.println("Error: unknown remove type '" + type + "'");
return -1;
}
private int listUidPolicies(String msg, int policy) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
final int[] uids = mInterface.getUidsWithPolicy(policy);
+ return listUidList(msg, uids);
+ }
+
+ private int listUidList(String msg, int[] uids) {
+ final PrintWriter pw = getOutPrintWriter();
pw.print(msg); pw.print(": ");
if (uids.length == 0) {
pw.println("none");
@@ -214,6 +228,12 @@
POLICY_REJECT_METERED_BACKGROUND);
}
+ private int listAppIdleWhitelist() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int[] uids = mInterface.getAppIdleWhitelist();
+ return listUidList("App Idle whitelisted UIDs", uids);
+ }
+
private int getRestrictBackground() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
pw.print("Restrict background status: ");
@@ -277,6 +297,23 @@
return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
}
+ private int setAppIdleWhitelist(boolean isWhitelisted) {
+ final int uid = getUidFromNextArg();
+ if (uid < 0) {
+ return uid;
+ }
+ mInterface.setAppIdleWhitelist(uid, isWhitelisted);
+ return 0;
+ }
+
+ private int addAppIdleWhitelist() throws RemoteException {
+ return setAppIdleWhitelist(true);
+ }
+
+ private int removeAppIdleWhitelist() throws RemoteException {
+ return setAppIdleWhitelist(false);
+ }
+
private int listWifiNetworks() {
final PrintWriter pw = getOutPrintWriter();
final String arg = getNextArg();
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/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index decdac6..84bb13e 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import android.app.Notification;
import android.service.notification.NotificationStats;
import com.android.internal.statusbar.NotificationVisibility;
@@ -26,7 +27,7 @@
void onNotificationClick(int callingUid, int callingPid, String key,
NotificationVisibility nv);
void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex,
- NotificationVisibility nv);
+ Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant);
void onNotificationClear(int callingUid, int callingPid,
String pkg, String tag, int id, int userId, String key,
@NotificationStats.DismissalSurface int dismissalSurface,
@@ -45,5 +46,15 @@
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
void onNotificationSmartRepliesAdded(String key, int replyCount);
- void onNotificationSmartReplySent(String key, int replyIndex);
+
+ /**
+ * Notifies a smart reply is sent.
+ *
+ * @param key the notification key
+ * @param clickedIndex the index of clicked reply
+ * @param reply the reply that is sent
+ * @param generatedByAssistant specifies is the reply generated by NAS
+ */
+ void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
+ boolean generatedByAssistant);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da29e4..95e1962 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,6 +198,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -246,6 +247,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Predicate;
/** {@hide} */
@@ -268,6 +270,7 @@
static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7;
+ static final int MESSAGE_ON_PACKAGE_CHANGED = 8;
// ranking thread messages
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
@@ -424,6 +427,7 @@
private GroupHelper mGroupHelper;
private int mAutoGroupAtCount;
private boolean mIsTelevision;
+ private boolean mIsAutomotive;
private MetricsLogger mMetricsLogger;
private Predicate<String> mAllowedManagedServicePackages;
@@ -599,6 +603,9 @@
}
}
+ /**
+ * Saves notification policy
+ */
public void savePolicyFile() {
mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
@@ -751,7 +758,8 @@
@Override
public void onNotificationActionClick(int callingUid, int callingPid, String key,
- int actionIndex, NotificationVisibility nv) {
+ int actionIndex, Notification.Action action, NotificationVisibility nv,
+ boolean generatedByAssistant) {
exitIdle();
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
@@ -771,6 +779,8 @@
nv.rank, nv.count);
nv.recycle();
reportUserInteraction(r);
+ mAssistants.notifyAssistantActionClicked(
+ r.sbn, actionIndex, action, generatedByAssistant);
}
}
@@ -887,6 +897,7 @@
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+ mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
}
}
}
@@ -902,6 +913,7 @@
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
+ mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
}
}
}
@@ -917,7 +929,8 @@
}
@Override
- public void onNotificationSmartReplySent(String key, int replyIndex) {
+ public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
+ boolean generatedByAssistant) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
@@ -927,6 +940,8 @@
mMetricsLogger.write(logMaker);
// Treat clicking on a smart reply as a user interaction.
reportUserInteraction(r);
+ mAssistants.notifyAssistantSuggestedReplySent(
+ r.sbn, reply, generatedByAssistant);
}
}
}
@@ -1123,12 +1138,7 @@
}
}
-
- mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
- mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
- mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
- mPreferencesHelper.onPackagesChanged(
- removingPackage, changeUserId, pkgList, uidList);
+ mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
savePolicyFile();
}
}
@@ -1384,6 +1394,11 @@
}
@VisibleForTesting
+ void setIsAutomotive(boolean isAutomotive) {
+ mIsAutomotive = isAutomotive;
+ }
+
+ @VisibleForTesting
void setIsTelevision(boolean isTelevision) {
mIsTelevision = isTelevision;
}
@@ -1539,6 +1554,9 @@
mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
|| mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
+
+ mIsAutomotive =
+ mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
}
@Override
@@ -4238,18 +4256,7 @@
// Fix the notification as best we can.
try {
- final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
- pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
- Notification.addFieldsFromContext(ai, notification);
-
- int canColorize = mPackageManagerClient.checkPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
- if (canColorize == PERMISSION_GRANTED) {
- notification.flags |= Notification.FLAG_CAN_COLORIZE;
- } else {
- notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
- }
+ fixNotification(notification, pkg, userId);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
@@ -4350,6 +4357,33 @@
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
+ @VisibleForTesting
+ protected void fixNotification(Notification notification, String pkg, int userId)
+ throws NameNotFoundException {
+ final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
+ pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
+ Notification.addFieldsFromContext(ai, notification);
+
+ int canColorize = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+ if (canColorize == PERMISSION_GRANTED) {
+ notification.flags |= Notification.FLAG_CAN_COLORIZE;
+ } else {
+ notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
+ }
+
+ if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
+ if (fullscreenIntentPermission != PERMISSION_GRANTED) {
+ notification.fullScreenIntent = null;
+ Log.w(TAG, "Package " + pkg +
+ ": Use of fullScreenIntent requires the USE_FULL_SCREEN_INTENT permission");
+ }
+ }
+ }
+
private void doChannelWarningToast(CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
@@ -4398,19 +4432,20 @@
*
* Has side effects.
*/
- private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
+ private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
NotificationRecord r, boolean isAutogroup) {
final String pkg = r.sbn.getPackageName();
final boolean isSystemNotification =
- isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
+ isUidSystemOrPhone(uid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
+ final int callingUid = Binder.getCallingUid();
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
+ && isCallerInstantApp(callingUid, userId)) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
// update notifications created by the system (e.g. a foreground service
@@ -4729,7 +4764,7 @@
mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
- mAssistants.onNotificationEnqueued(r);
+ mAssistants.onNotificationEnqueuedLocked(r);
mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
DELAY_FOR_ASSISTANT_TIME);
} else {
@@ -5110,7 +5145,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);
@@ -5681,6 +5718,16 @@
}
}
+ private void handleOnPackageChanged(boolean removingPackage, int changeUserId,
+ String[] pkgList, int[] uidList) {
+ mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
+ mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
+ mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
+ mPreferencesHelper.onPackagesChanged(
+ removingPackage, changeUserId, pkgList, uidList);
+ handleSavePolicyFile();
+ }
+
protected class WorkerHandler extends Handler
{
public WorkerHandler(Looper looper) {
@@ -5693,10 +5740,10 @@
switch (msg.what)
{
case MESSAGE_DURATION_REACHED:
- handleDurationReached((ToastRecord)msg.obj);
+ handleDurationReached((ToastRecord) msg.obj);
break;
case MESSAGE_FINISH_TOKEN_TIMEOUT:
- handleKillTokenTimeout((ToastRecord)msg.obj);
+ handleKillTokenTimeout((ToastRecord) msg.obj);
break;
case MESSAGE_SAVE_POLICY_FILE:
handleSavePolicyFile();
@@ -5710,6 +5757,12 @@
case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
handleListenerInterruptionFilterChanged(msg.arg1);
break;
+ case MESSAGE_ON_PACKAGE_CHANGED:
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleOnPackageChanged((boolean) args.arg1, args.argi1, (String[]) args.arg2,
+ (int[]) args.arg3);
+ args.recycle();
+ break;
}
}
@@ -5725,6 +5778,16 @@
sendMessage(Message.obtain(this, cancelRunnable));
}
}
+
+ protected void scheduleOnPackageChanged(boolean removingPackage, int changeUserId,
+ String[] pkgList, int[] uidList) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = removingPackage;
+ args.argi1 = changeUserId;
+ args.arg2 = pkgList;
+ args.arg3 = uidList;
+ sendMessage(Message.obtain(this, MESSAGE_ON_PACKAGE_CHANGED, args));
+ }
}
private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -6521,24 +6584,28 @@
}
@VisibleForTesting
- boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
+ boolean isCallerInstantApp(int callingUid, int userId) {
// System is always allowed to act for ephemeral apps.
if (isUidSystemOrPhone(callingUid)) {
return false;
}
- mAppOps.checkPackage(callingUid, pkg);
-
try {
+ final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+ if (pkgs == null) {
+ throw new SecurityException("Unknown uid " + callingUid);
+ }
+ final String pkg = pkgs[0];
+ mAppOps.checkPackage(callingUid, pkg);
+
ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
return ai.isInstantApp();
} catch (RemoteException re) {
- throw new SecurityException("Unknown package " + pkg, re);
+ throw new SecurityException("Unknown uid " + callingUid, re);
}
-
}
private void checkCallerIsSameApp(String pkg) {
@@ -6842,38 +6909,96 @@
}
}
- public void onNotificationEnqueued(final NotificationRecord r) {
+ @GuardedBy("mNotificationLock")
+ private void onNotificationEnqueuedLocked(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
- TrimCache trimCache = new TrimCache(sbn);
-
- // There should be only one, but it's a list, so while we enforce
- // singularity elsewhere, we keep it general here, to avoid surprises.
- for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info)
- && info.isSameUser(r.getUserId());
- if (!sbnVisible) {
- continue;
- }
-
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyEnqueued(info, sbnToPost, r.getChannel());
- }
- });
- }
+ notifyAssistantLocked(
+ sbn,
+ true /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ });
}
- private void notifyEnqueued(final ManagedServiceInfo info,
- final StatusBarNotification sbn, final NotificationChannel channel) {
- final INotificationListener assistant = (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
- try {
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, channel);
- } catch (RemoteException ex) {
- Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
- }
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantExpansionChangedLocked(
+ final StatusBarNotification sbn,
+ final boolean isUserAction,
+ final boolean isExpanded) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantNotificationDirectReplyLocked(
+ final StatusBarNotification sbn) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationDirectReply(key);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantSuggestedReplySent(
+ final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onSuggestedReplySent(
+ key,
+ reply,
+ generatedByAssistant
+ ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+ : NotificationAssistantService.SOURCE_FROM_APP);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantActionClicked(
+ final StatusBarNotification sbn, int actionIndex, Notification.Action action,
+ boolean generatedByAssistant) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onActionClicked(
+ key,
+ action,
+ generatedByAssistant
+ ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+ : NotificationAssistantService.SOURCE_FROM_APP);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+ }
+ });
}
/**
@@ -6881,30 +7006,49 @@
* context
*/
@GuardedBy("mNotificationLock")
- public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
- final String snoozeCriterionId) {
- TrimCache trimCache = new TrimCache(sbn);
- for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info);
- if (!sbnVisible) {
- continue;
- }
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final INotificationListener assistant =
- (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder
- = new StatusBarNotificationHolder(sbnToPost);
+ private void notifyAssistantSnoozedLocked(
+ final StatusBarNotification sbn, final String snoozeCriterionId) {
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
try {
assistant.onNotificationSnoozedUntilContext(
sbnHolder, snoozeCriterionId);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
}
- }
- });
+ });
+ }
+
+ /**
+ * Notifies the assistant something about the specified notification, only assistant
+ * that is visible to the notification will be notified.
+ *
+ * @param sbn the notification object that the update is about.
+ * @param sameUserOnly should the update be sent to the assistant in the same user only.
+ * @param callback the callback that provides the assistant to be notified, executed
+ * in WorkerHandler.
+ */
+ @GuardedBy("mNotificationLock")
+ private void notifyAssistantLocked(
+ final StatusBarNotification sbn,
+ boolean sameUserOnly,
+ BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+ TrimCache trimCache = new TrimCache(sbn);
+ // There should be only one, but it's a list, so while we enforce
+ // singularity elsewhere, we keep it general here, to avoid surprises.
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info)
+ && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
+ if (!sbnVisible) {
+ continue;
+ }
+ final INotificationListener assistant = (INotificationListener) info.service;
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ mHandler.post(() -> callback.accept(assistant, sbnHolder));
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f279af0..94d276c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -309,6 +309,7 @@
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
+ newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
diff --git a/services/core/java/com/android/server/oemlock/OemLock.java b/services/core/java/com/android/server/oemlock/OemLock.java
index ee70c29..352884b 100644
--- a/services/core/java/com/android/server/oemlock/OemLock.java
+++ b/services/core/java/com/android/server/oemlock/OemLock.java
@@ -19,6 +19,9 @@
import android.annotation.Nullable;
abstract class OemLock {
+ @Nullable
+ abstract String getLockName();
+
abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature);
abstract boolean isOemUnlockAllowedByCarrier();
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index a6200bf..6e82c24 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -113,6 +113,19 @@
*/
private final IBinder mService = new IOemLockService.Stub() {
@Override
+ @Nullable
+ public String getLockName() {
+ enforceManageCarrierOemUnlockPermission();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mOemLock.getLockName();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
enforceManageCarrierOemUnlockPermission();
enforceUserIsAdmin();
diff --git a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
index d9362d4..a1c27d6 100644
--- a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
+++ b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
@@ -40,6 +40,12 @@
}
@Override
+ @Nullable
+ String getLockName() {
+ return "";
+ }
+
+ @Override
void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
// Note: this implementation does not require a signature
if (signature != null) {
diff --git a/services/core/java/com/android/server/oemlock/VendorLock.java b/services/core/java/com/android/server/oemlock/VendorLock.java
index 1b9de36..37540d0 100644
--- a/services/core/java/com/android/server/oemlock/VendorLock.java
+++ b/services/core/java/com/android/server/oemlock/VendorLock.java
@@ -22,8 +22,6 @@
import android.hardware.oemlock.V1_0.OemLockSecureStatus;
import android.hardware.oemlock.V1_0.OemLockStatus;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Slog;
import java.util.ArrayList;
@@ -55,14 +53,49 @@
}
@Override
+ @Nullable
+ String getLockName() {
+ final Integer[] requestStatus = new Integer[1];
+ final String[] lockName = new String[1];
+
+ try {
+ mOemLock.getName((status, name) -> {
+ requestStatus[0] = status;
+ lockName[0] = name;
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get name from HAL", e);
+ throw e.rethrowFromSystemServer();
+ }
+
+ switch (requestStatus[0]) {
+ case OemLockStatus.OK:
+ // Success
+ return lockName[0];
+
+ case OemLockStatus.FAILED:
+ Slog.e(TAG, "Failed to get OEM lock name.");
+ return null;
+
+ default:
+ Slog.e(TAG, "Unknown return value indicates code is out of sync with HAL");
+ return null;
+ }
+ }
+
+ @Override
void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
try {
- switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, toByteArrayList(signature))) {
+ ArrayList<Byte> signatureBytes = toByteArrayList(signature);
+ switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, signatureBytes)) {
case OemLockSecureStatus.OK:
Slog.i(TAG, "Updated carrier allows OEM lock state to: " + allowed);
return;
case OemLockSecureStatus.INVALID_SIGNATURE:
+ if (signatureBytes.isEmpty()) {
+ throw new IllegalArgumentException("Signature required for carrier unlock");
+ }
throw new SecurityException(
"Invalid signature used in attempt to carrier unlock");
@@ -154,9 +187,9 @@
}
}
- private ArrayList toByteArrayList(byte[] data) {
+ private ArrayList<Byte> toByteArrayList(byte[] data) {
if (data == null) {
- return null;
+ return new ArrayList<Byte>();
}
ArrayList<Byte> result = new ArrayList<Byte>(data.length);
for (final byte b : data) {
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 731e6bc..6d59827 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -46,7 +46,7 @@
* Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
*/
class IdmapManager {
- private static final boolean FEATURE_FLAG_IDMAP2 = false;
+ private static final boolean FEATURE_FLAG_IDMAP2 = true;
private final Installer mInstaller;
private IIdmap2 mIdmap2Service;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1b03d1..d471904 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -22,14 +22,11 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityThread;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,7 +34,6 @@
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
@@ -59,11 +55,9 @@
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.util.ConcurrentUtils;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerService;
@@ -84,8 +78,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -228,8 +220,6 @@
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
- private Future<?> mInitCompleteSignal;
-
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
@@ -241,29 +231,28 @@
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
getDefaultOverlayPackages(), new OverlayChangeListener());
- mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, null);
- final IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(ACTION_USER_ADDED);
- userFilter.addAction(ACTION_USER_REMOVED);
- getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
- userFilter, null, null);
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+ packageFilter, null, null);
- restoreSettings();
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(ACTION_USER_ADDED);
+ userFilter.addAction(ACTION_USER_REMOVED);
+ getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
+ userFilter, null, null);
- initIfNeeded();
- onSwitchUser(UserHandle.USER_SYSTEM);
+ restoreSettings();
- publishBinderService(Context.OVERLAY_SERVICE, mService);
- publishLocalService(OverlayManagerService.class, this);
- }, "Init OverlayManagerService");
+ initIfNeeded();
+ onSwitchUser(UserHandle.USER_SYSTEM);
+
+ publishBinderService(Context.OVERLAY_SERVICE, mService);
+ publishLocalService(OverlayManagerService.class, this);
}
@Override
@@ -271,32 +260,6 @@
// Intentionally left empty.
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
- "Wait for OverlayManagerService init");
- mInitCompleteSignal = null;
- }
- }
-
- public void updateSystemUiContext() {
- if (mInitCompleteSignal != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
- "Wait for OverlayManagerService init");
- mInitCompleteSignal = null;
- }
-
- final ApplicationInfo ai;
- try {
- ai = mPackageManager.mPackageManager.getApplicationInfo("android",
- GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
- }
-
private void initIfNeeded() {
final UserManager um = getContext().getSystemService(UserManager.class);
final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b490381..7f1fb6c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -310,7 +310,7 @@
.setPackage(packageName),
user);
if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 0) == 0) {
+ Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
return launcherActivities;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cc640f0..093b85e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,6 +16,28 @@
package com.android.server.pm;
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
+import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -41,6 +63,8 @@
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
+import dalvik.system.DexFile;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -48,32 +72,6 @@
import java.util.List;
import java.util.Map;
-import dalvik.system.DexFile;
-
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
-
-import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
-import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
-import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
-import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
-import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
-import static com.android.server.pm.Installer.DEXOPT_FORCE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
-import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-
-import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
-
-import static dalvik.system.DexFile.getSafeModeCompilerFilter;
-import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
-
/**
* Helper class for running dexopt command on packages.
*/
@@ -544,8 +542,7 @@
// The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
// the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() &&
- (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
+ boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// Some apps are executed with restrictions on hidden API usage. If this app is one
// of them, pass a flag to dexopt to enable the same restrictions during compilation.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1a5b86c..2e9a71a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -455,12 +455,6 @@
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
- if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
- || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- throw new IllegalArgumentException(
- "New installs into ASEC containers no longer supported");
- }
-
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -487,21 +481,10 @@
if (!PackageHelper.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
-
- } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- if (!PackageHelper.fitsOnExternal(mContext, params)) {
- throw new IOException("No suitable external storage available");
- }
-
- } 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.setInstallFlagsInternal();
-
} else {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
- params.setInstallFlagsInternal();
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index acbd81d..dca92ea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,7 +43,6 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -54,12 +53,12 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
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;
@@ -118,8 +117,7 @@
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState
- .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
@@ -199,6 +197,7 @@
import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UsesPermissionInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
@@ -315,8 +314,7 @@
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
- .DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionManagerService;
@@ -375,6 +373,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -646,9 +645,6 @@
/** Directory where installed application's 32-bit native libraries are copied. */
private static final File sAppLib32InstallDir =
new File(Environment.getDataDirectory(), "app-lib");
- /** Directory where code and non-resource assets of forward-locked applications are stored */
- private static final File sDrmAppPrivateInstallDir =
- new File(Environment.getDataDirectory(), "app-private");
// ----------------------------------------------------------------
@@ -1811,12 +1807,10 @@
firstUserIds, firstInstantUserIds);
}
- // Send broadcast package appeared if forward locked/external for all users
- // treat asec-hosted packages like removable media on upgrade
- if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+ // Send broadcast package appeared if external for all users
+ if (isExternal(res.pkg)) {
if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg
- + " is ASEC-hosted -> AVAILABLE");
+ Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
}
final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
ArrayList<String> pkgList = new ArrayList<>(1);
@@ -2629,10 +2623,6 @@
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
- scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
- | PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
@@ -5407,7 +5397,7 @@
synchronized (mPackages) {
Signature[] s1;
Signature[] s2;
- Object obj = mSettings.getUserIdLPr(uid1);
+ Object obj = mSettings.getSettingLPr(uid1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
@@ -5426,7 +5416,7 @@
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- obj = mSettings.getUserIdLPr(uid2);
+ obj = mSettings.getSettingLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
@@ -5485,7 +5475,7 @@
// reader
synchronized (mPackages) {
final PackageParser.SigningDetails signingDetails;
- final Object obj = mSettings.getUserIdLPr(uid);
+ final Object obj = mSettings.getSettingLPr(uid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
@@ -5598,7 +5588,7 @@
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getSettingLPr(uid);
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return null;
@@ -5634,7 +5624,7 @@
return null;
}
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -5662,7 +5652,7 @@
synchronized (mPackages) {
for (int i = uids.length - 1; i >= 0; i--) {
final int uid = uids[i];
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
names[i] = "shared:" + sus.name;
@@ -5711,7 +5701,7 @@
return 0;
}
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgFlags;
@@ -5733,7 +5723,7 @@
return 0;
}
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgPrivateFlags;
@@ -5756,7 +5746,7 @@
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getSettingLPr(uid);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
final Iterator<PackageSetting> it = sus.packages.iterator();
@@ -6396,7 +6386,7 @@
callingUid = mIsolatedOwners.get(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
- final Object obj = mSettings.getUserIdLPr(appId);
+ final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
@@ -8631,7 +8621,7 @@
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
final InstallArgs args = createInstallArgsForExisting(
- packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
args.cleanUpResourcesLI();
synchronized (mPackages) {
@@ -8641,16 +8631,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());
@@ -8704,7 +8686,7 @@
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
InstallArgs args = createInstallArgsForExisting(
- packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
@@ -8726,7 +8708,13 @@
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (scanResult.success) {
synchronized (mPackages) {
- commitScanResultLocked(scanResult);
+ try {
+ prepareScanResultLocked(scanResult);
+ commitScanResultLocked(scanResult);
+ } catch (PackageManagerException e) {
+ unprepareScanResultLocked(scanResult);
+ throw e;
+ }
}
}
@@ -10125,7 +10113,37 @@
}
}
for (ScanResult result : results) {
- commitScanResultLocked(result);
+ try {
+ prepareScanResultLocked(result);
+ commitScanResultLocked(result);
+ } catch (PackageManagerException e) {
+ unprepareScanResultLocked(result);
+ throw e;
+ }
+ }
+ }
+ }
+
+ /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
+ private void prepareScanResultLocked(@NonNull ScanResult result)
+ throws PackageManagerException {
+ if (!result.existingSettingCopied) {
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ mSettings.registerAppIdLPw(result.pkgSetting);
+ }
+ }
+
+ /**
+ * Reverts any changes to the system that were made by
+ * {@link #prepareScanResultLocked(ScanResult)}
+ */
+ private void unprepareScanResultLocked(@NonNull ScanResult result) {
+ if (!result.existingSettingCopied) {
+ // iff we've acquired an app ID for a new package setting, remove it so that it can be
+ // acquired by another request.
+ if (result.pkgSetting.appId > 0) {
+ mSettings.removeAppIdLPw(result.pkgSetting.appId);
}
}
}
@@ -10164,10 +10182,6 @@
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
}
- // THROWS: when we can't allocate a user id. add call to check if there's
- // enough space to ensure we won't throw; otherwise, don't modify state
- mSettings.addUserToSettingLPw(pkgSetting);
-
if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
mTransferedPackages.add(originalPkgSetting.name);
}
@@ -10278,12 +10292,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;
@@ -11221,6 +11244,26 @@
}
}
}
+
+ // Check permission usage info requirements.
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ for (UsesPermissionInfo upi : pkg.usesPermissionInfos) {
+ if (!mPermissionManager.isPermissionUsageInfoRequired(upi.getPermission())) {
+ continue;
+ }
+ if (upi.getDataSentOffDevice() == UsesPermissionInfo.USAGE_UNDEFINED
+ || upi.getDataSharedWithThirdParty()
+ == UsesPermissionInfo.USAGE_UNDEFINED
+ || upi.getDataUsedForMonetization()
+ == UsesPermissionInfo.USAGE_UNDEFINED
+ || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) {
+ // STOPSHIP: Make this throw
+ Slog.wtf(TAG, "Package " + pkg.packageName + " does not provide usage "
+ + "information for permission " + upi.getPermission()
+ + ". This will be a fatal error in Q.");
+ }
+ }
+ }
}
}
@@ -11536,11 +11579,8 @@
// pass once we've determined ABI below.
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
- // We would never need to extract libs for forward-locked and external packages,
- // since the container service will do it for us. We shouldn't attempt to
- // extract libs from system app when it was not updated.
- if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
- (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
+ // We shouldn't attempt to extract libs from system app when it was not updated.
+ if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
extractLibs = false;
}
@@ -11881,7 +11921,6 @@
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
- final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
info.nativeLibraryRootDir = null;
info.nativeLibraryRootRequiresIsa = false;
@@ -11910,9 +11949,6 @@
info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
secondaryLibDir, apkName).getAbsolutePath();
}
- } else if (asecApp) {
- info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
- .getAbsolutePath();
} else {
final String apkName = deriveCodePathName(codePath);
info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
@@ -13496,7 +13532,7 @@
}
Signature[] callerSignature;
- Object obj = mSettings.getUserIdLPr(callingUid);
+ Object obj = mSettings.getSettingLPr(callingUid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
@@ -14021,7 +14057,6 @@
private int installLocationPolicy(PackageInfoLite pkgLite) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
- boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
// reader
synchronized (mPackages) {
// Currently installed package which the new package is attempting to replace or
@@ -14074,16 +14109,8 @@
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (onSd) {
- Slog.w(TAG, "Cannot install update to system app on sdcard");
- return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
- }
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else {
- if (onSd) {
- // Install flag overrides everything.
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
// If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
@@ -14104,11 +14131,6 @@
}
}
}
- // All the special cases have been taken care of.
- // Return result based on recommended install location.
- if (onSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
return pkgLite.recommendedInstallLocation;
}
@@ -14125,70 +14147,59 @@
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
- final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
- if (onInt && onSd) {
- // Check if both bits are set.
- Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else if (onSd && ephemeral) {
- Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else {
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
- if (DEBUG_INSTANT && ephemeral) {
- Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+
+ if (DEBUG_INSTANT && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
+ /*
+ * If we have too little free space, try to free cache
+ * before giving up.
+ */
+ if (!origin.staged && pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+ origin.resolvedPath, packageAbiOverride);
+ if (sizeBytes >= 0) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
}
/*
- * If we have too little free space, try to free cache
- * before giving up.
+ * The cache free must have deleted the file we downloaded to install.
+ *
+ * TODO: fix the "freeCache" call to not delete the file we care about.
*/
- if (!origin.staged && pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- // TODO: focus freeing disk space on the target device
- final StorageManager storage = StorageManager.from(mContext);
- final long lowThreshold = storage.getStorageLowBytes(
- Environment.getDataDirectory());
-
- final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
- origin.resolvedPath, packageAbiOverride);
- if (sizeBytes >= 0) {
- try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
- }
- }
-
- /*
- * The cache free must have deleted the file we
- * downloaded to install.
- *
- * TODO: fix the "freeCache" call to not delete
- * the file we care about.
- */
- if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- pkgLite.recommendedInstallLocation
+ if (pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+ pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
- }
}
}
+
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
@@ -14208,24 +14219,21 @@
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
- } else if (!onSd && !onInt) {
+ } else if (!onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
- installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
}
installFlags |= PackageManager.INSTALL_INSTANT_APP;
- installFlags &= ~(PackageManager.INSTALL_EXTERNAL
- |PackageManager.INSTALL_INTERNAL);
+ installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external
// media is unset
installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
}
}
@@ -14409,7 +14417,7 @@
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
*/
- private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
+ private InstallArgs createInstallArgsForExisting(String codePath,
String resourcePath, String[] instructionSets) {
return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
@@ -14501,14 +14509,6 @@
return PackageManager.INSTALL_SUCCEEDED;
}
- protected boolean isFwdLocked() {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- }
-
- protected boolean isExternalAsec() {
- return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
- }
-
protected boolean isEphemeral() {
return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
}
@@ -14536,7 +14536,7 @@
}
/**
- * Logic to handle installation of non-ASEC applications, including copying
+ * Logic to handle installation of new applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
@@ -14558,9 +14558,6 @@
params.grantedRuntimePermissions,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.mParentInstallParams);
- if (isFwdLocked()) {
- throw new IllegalArgumentException("Forward locking only supported in ASEC");
- }
}
/** Existing install */
@@ -15215,8 +15212,11 @@
}
}
private static class ReconcileFailure extends PackageManagerException {
- public ReconcileFailure(String message) {
- super("Invalid reconcile request: " + message);
+ ReconcileFailure(String message) {
+ super("Reconcile failed: " + message);
+ }
+ ReconcileFailure(int reason, String message) {
+ super(reason, "Reconcile failed: " + message);
}
}
@@ -15235,10 +15235,12 @@
@PackageManager.InstallFlags
public final int installFlags;
public final InstallArgs installArgs;
+ public final DeletePackageAction deletePackageAction;
private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
- String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+ String volumeUuid, PrepareResult prepareResult, ScanResult scanResult,
+ DeletePackageAction deletePackageAction) {
this.installArgs = installArgs;
this.pkgSetting = pkgSetting;
this.installForUser = installForUser;
@@ -15247,6 +15249,7 @@
this.volumeUuid = volumeUuid;
this.prepareResult = prepareResult;
this.scanResult = scanResult;
+ this.deletePackageAction = deletePackageAction;
}
}
@@ -15259,14 +15262,30 @@
final ScanResult scanResult = request.scannedPackages.get(installPackageName);
final InstallArgs installArgs = request.installArgs.get(installPackageName);
final PackageInstalledInfo res = request.installResults.get(installPackageName);
+ final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
if (scanResult == null || installArgs == null || res == null) {
throw new ReconcileFailure(
"inputs not balanced; missing argument for " + installPackageName);
}
+ final DeletePackageAction deletePackageAction;
+ // we only want to try to delete for non system apps
+ if (prepareResult.replace && !prepareResult.system) {
+ deletePackageAction = mayDeletePackageLocked(res.removedInfo,
+ prepareResult.originalPs, prepareResult.disabledPs,
+ prepareResult.childPackageSettings);
+ if (deletePackageAction == null) {
+ throw new ReconcileFailure(
+ PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "May not delete " + installPackageName + " to replace");
+ }
+ } else {
+ deletePackageAction = null;
+ }
result.put(installPackageName,
new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
res, installArgs.installFlags, installArgs.volumeUuid,
- request.preparedPackages.get(installPackageName), scanResult));
+ request.preparedPackages.get(installPackageName), scanResult,
+ deletePackageAction));
}
return result;
}
@@ -15291,7 +15310,7 @@
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
- res.removedInfo.args = createInstallArgsForExisting(0,
+ res.removedInfo.args = createInstallArgsForExisting(
oldPackage.applicationInfo.getCodePath(),
oldPackage.applicationInfo.getResourcePath(),
getAppDexInstructionSets(oldPackage.applicationInfo));
@@ -15338,29 +15357,29 @@
final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- // First delete the existing package while retaining the data directory
- if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
- res.removedInfo, true, pkg)) {
- // If the existing package wasn't successfully deleted
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "replaceNonSystemPackageLI");
- return false;
- } else {
- // Successfully deleted the old package; proceed with replace.
-
- // If deleted package lived in a container, give users a chance to
- // relinquish resources before killing.
- if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + oldPackage
- + " is ASEC-hosted -> UNAVAILABLE");
- }
- final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
- final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(oldPackage.applicationInfo.packageName);
- sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ try {
+ executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
+ null, true, request.mAllUsers, deleteFlags, true, pkg);
+ } catch (SystemDeleteException e) {
+ if (Build.IS_ENG) {
+ throw new RuntimeException("Unexpected failure", e);
+ // ignore; not possible for non-system app
}
}
+ // Successfully deleted the old package; proceed with replace.
+
+ // If deleted package lived in a container, give users a chance to
+ // relinquish resources before killing.
+ if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + oldPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+ final ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(oldPackage.applicationInfo.packageName);
+ sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ }
// Update the in-memory copy of the previous code paths.
PackageSetting ps1 = mSettings.mPackages.get(
@@ -15414,8 +15433,10 @@
try {
+ prepareScanResultLocked(scanResult);
commitScanResultLocked(scanResult);
} catch (PackageManagerException e) {
+ unprepareScanResultLocked(scanResult);
res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
res.setError("Package couldn't be installed in " + pkg.codePath, e);
return false;
@@ -15570,8 +15591,6 @@
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
- final boolean forwardLocked =
- ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
final boolean instantApp =
((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
@@ -15604,10 +15623,8 @@
// This update happens in place!
//
// We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not forward locked.
- // 2) it is not on on an external ASEC container.
- // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 4) it is not debuggable.
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
//
// Note that we do not dexopt instant apps by default. dexopt can take some time to
// complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15615,9 +15632,8 @@
// continuous progress to the useur instead of mysteriously blocking somewhere in the
// middle of running an instant app. The default behaviour can be overridden
// via gservices.
- final boolean performDexopt = !forwardLocked
- && !pkg.applicationInfo.isExternalAsec()
- && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ final boolean performDexopt =
+ (!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
@@ -15712,7 +15728,6 @@
// Parse old package
boolean oldExternal = isExternal(oldPackage);
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
- | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
@@ -15772,13 +15787,16 @@
@Nullable
public final String renamedPackage;
public final PackageFreezer freezer;
+ public final PackageSetting originalPs;
+ public final PackageSetting disabledPs;
+ public final PackageSetting[] childPackageSettings;
private PrepareResult(int installReason, String volumeUuid,
String installerPackageName, UserHandle user, boolean replace, int scanFlags,
int parseFlags, PackageParser.Package existingPackage,
PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
- String renamedPackage,
- PackageFreezer freezer) {
+ String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+ PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
this.installReason = installReason;
this.volumeUuid = volumeUuid;
this.installerPackageName = installerPackageName;
@@ -15792,6 +15810,9 @@
this.system = system;
this.renamedPackage = renamedPackage;
this.freezer = freezer;
+ this.originalPs = originalPs;
+ this.disabledPs = disabledPs;
+ this.childPackageSettings = childPackageSettings;
}
}
@@ -15830,9 +15851,7 @@
final String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
final File tmpPackageFile = new File(args.getCodePath());
- final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
- final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
- || (args.volumeUuid != null));
+ final boolean onExternal = args.volumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
@@ -15859,16 +15878,14 @@
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Sanity check
- if (instantApp && (forwardLocked || onExternal)) {
- Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
- + " external=" + onExternal);
+ if (instantApp && onExternal) {
+ Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
// Retrieve PackageSettings and parse package
@ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE
- | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
@@ -16213,7 +16230,7 @@
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
- } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+ } else {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
@@ -16299,7 +16316,9 @@
String targetVolumeUuid = volumeUuid;
int targetScanFlags = scanFlags;
int targetParseFlags = parseFlags;
-
+ final PackageSetting ps;
+ final PackageSetting disabledPs;
+ final PackageSetting[] childPackages;
if (replace) {
targetVolumeUuid = null;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
@@ -16319,7 +16338,6 @@
final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final PackageParser.Package oldPackage;
- final PackageSetting ps;
final String pkgName11 = pkg.packageName;
final int[] allUsers;
final int[] installedUsers;
@@ -16346,6 +16364,7 @@
}
ps = mSettings.mPackages.get(pkgName11);
+ disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
// verify signatures are valid
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -16442,49 +16461,52 @@
res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
}
- final int childCount = (oldPackage.childPackages != null)
- ? oldPackage.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- boolean childPackageUpdated = false;
- PackageParser.Package childPkg = oldPackage.childPackages.get(i);
- final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
- if (res.addedChildPackages != null) {
- PackageInstalledInfo childRes = res.addedChildPackages.get(
- childPkg.packageName);
- if (childRes != null) {
- childRes.removedInfo.uid = childPkg.applicationInfo.uid;
- childRes.removedInfo.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRes.removedInfo.installerPackageName =
- childPs.installerPackageName;
- }
- childRes.removedInfo.isUpdate = true;
- childRes.removedInfo.installReasons = res.removedInfo.installReasons;
- childPackageUpdated = true;
- }
- }
- if (!childPackageUpdated) {
- PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
- childRemovedRes.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRemovedRes.installerPackageName = childPs.installerPackageName;
- }
- childRemovedRes.isUpdate = false;
- childRemovedRes.dataRemoved = true;
- synchronized (mPackages) {
- if (childPs != null) {
- childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
- true);
+ childPackages = mSettings.getChildSettingsLPr(ps);
+ if (childPackages != null) {
+ for (PackageSetting childPs : childPackages) {
+ boolean childPackageUpdated = false;
+ PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg;
+ if (res.addedChildPackages != null) {
+ PackageInstalledInfo childRes = res.addedChildPackages.get(
+ childPkg.packageName);
+ if (childRes != null) {
+ childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+ childRes.removedInfo.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRes.removedInfo.installerPackageName =
+ childPs.installerPackageName;
+ }
+ childRes.removedInfo.isUpdate = true;
+ childRes.removedInfo.installReasons =
+ res.removedInfo.installReasons;
+ childPackageUpdated = true;
}
}
- if (res.removedInfo.removedChildPackages == null) {
- res.removedInfo.removedChildPackages = new ArrayMap<>();
+ if (!childPackageUpdated) {
+ PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+ childRemovedRes.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRemovedRes.installerPackageName = childPs.installerPackageName;
+ }
+ childRemovedRes.isUpdate = false;
+ childRemovedRes.dataRemoved = true;
+ synchronized (mPackages) {
+ if (childPs != null) {
+ childRemovedRes.origUsers = childPs.queryInstalledUsers(
+ allUsers,
+ true);
+ }
+ }
+ if (res.removedInfo.removedChildPackages == null) {
+ res.removedInfo.removedChildPackages = new ArrayMap<>();
+ }
+ res.removedInfo.removedChildPackages.put(childPkg.packageName,
+ childRemovedRes);
}
- res.removedInfo.removedChildPackages.put(childPkg.packageName,
- childRemovedRes);
}
}
+
sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
@@ -16527,6 +16549,9 @@
}
} else { // new package install
+ ps = null;
+ childPackages = null;
+ disabledPs = null;
replace = false;
existingPackage = null;
// Remember this for later, in case we need to rollback this install
@@ -16557,9 +16582,11 @@
}
// we're passing the freezer back to be closed in a later phase of install
shouldCloseFreezerBeforeReturn = false;
+
return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
- replace /* clearCodeCache */, sysPkg, renamedPackage, freezer);
+ replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+ ps, disabledPs, childPackages);
} finally {
if (shouldCloseFreezerBeforeReturn) {
freezer.close();
@@ -16745,19 +16772,6 @@
return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
- private int packageFlagsToInstallFlags(PackageSetting ps) {
- int installFlags = 0;
- if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
- // This existing package was an external ASEC install when we have
- // the external flag without a UUID
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- }
- if (ps.isForwardLocked()) {
- installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- }
- return installFlags;
- }
-
private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -16773,9 +16787,6 @@
private void deleteTempPackageFiles() {
final FilenameFilter filter =
(dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
- for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
- file.delete();
- }
}
@Override
@@ -17525,34 +17536,20 @@
/*
* Tries to delete system package.
*/
- private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg,
- PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
- boolean writeSettings) {
- if (deletedPs.parentPackageName != null) {
- Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
- return false;
- }
-
+ private void deleteSystemPackageLIF(DeletePackageAction action,
+ PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles,
+ int flags, PackageRemovedInfo outInfo, boolean writeSettings)
+ throws SystemDeleteException {
final boolean applyUserRestrictions
= (allUserHandles != null) && (outInfo.origUsers != null);
- final PackageSetting disabledPs;
// 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
- synchronized (mPackages) {
- disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);
- }
-
+ final PackageSetting disabledPs = action.disabledPs;
if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
+ " disabledPs=" + disabledPs);
-
- if (disabledPs == null) {
- Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
- return false;
- } else if (DEBUG_REMOVE) {
- Slog.d(TAG, "Deleting system pkg from data partition");
- }
+ Slog.d(TAG, "Deleting system pkg from data partition");
if (DEBUG_REMOVE) {
if (applyUserRestrictions) {
@@ -17590,11 +17587,8 @@
flags |= PackageManager.DELETE_KEEP_DATA;
}
- boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+ deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
outInfo, writeSettings, disabledPs.pkg);
- if (!ret) {
- return false;
- }
// writer
synchronized (mPackages) {
@@ -17611,25 +17605,25 @@
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
- installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles,
+ installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+ e.getMessage());
- return false;
+ // TODO(patb): can we avoid this; throw would come from scan...
+ throw new SystemDeleteException(e);
} finally {
if (disabledPs.pkg.isStub) {
mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
}
}
- return true;
}
/**
* Installs a package that's already on the system partition.
*/
private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
- boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
+ @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
@Nullable PermissionsState origPermissionState, boolean writeSettings)
throws PackageManagerException {
@ParseFlags int parseFlags =
@@ -17637,7 +17631,7 @@
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- if (isPrivileged || locationIsPrivileged(codePathString)) {
+ if (locationIsPrivileged(codePathString)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
if (locationIsOem(codePathString)) {
@@ -17713,7 +17707,7 @@
return pkg;
}
- private boolean deleteInstalledPackageLIF(PackageSetting ps,
+ private void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, int[] allUserHandles,
PackageRemovedInfo outInfo, boolean writeSettings,
PackageParser.Package replacingPackage) {
@@ -17728,9 +17722,6 @@
for (int i = 0; i < childCount; i++) {
String childPackageName = ps.childPackageNames.get(i);
PackageSetting childPs = mSettings.mPackages.get(childPackageName);
- if (childPs == null) {
- return false;
- }
PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
childPackageName);
if (childInfo != null) {
@@ -17766,13 +17757,11 @@
// Delete application code and resources only for parent packages
if (ps.parentPackageName == null) {
if (deleteCodeAndResources && (outInfo != null)) {
- outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+ outInfo.args = createInstallArgsForExisting(
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
}
-
- return true;
}
@Override
@@ -17828,26 +17817,59 @@
private static class DeletePackageAction {
public final PackageSetting deletingPs;
+ public final PackageSetting disabledPs;
+ public final PackageRemovedInfo outInfo;
- private DeletePackageAction(PackageSetting deletingPs) {
+ private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
+ PackageRemovedInfo outInfo) {
this.deletingPs = deletingPs;
+ this.disabledPs = disabledPs;
+ this.outInfo = outInfo;
}
}
/**
- * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null}
- * otherwise.
+ * @return a {@link DeletePackageAction} if the provided package and related state may be
+ * deleted, {@code null} otherwise.
*/
@Nullable
- private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) {
- synchronized (mPackages) {
- final PackageSetting ps;
- ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
+ @GuardedBy("mPackages")
+ private static DeletePackageAction mayDeletePackageLocked(
+ PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
+ @Nullable PackageSetting[] children) {
+ if (ps == null) {
+ return null;
+ }
+ if (isSystemApp(ps)) {
+ if (ps.parentPackageName != null) {
+ Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
- return new DeletePackageAction(ps);
+
+ // 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);
+ return null;
+ }
}
+ final int parentReferenceCount =
+ (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
+ final int childCount = children != null ? children.length : 0;
+ if (childCount != parentReferenceCount) {
+ return null;
+ }
+ if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) {
+ for (PackageSetting child : children) {
+ if (child == null || !ps.childPackageNames.contains(child.name)) {
+ return null;
+ }
+ }
+ }
+ return new DeletePackageAction(ps, disabledPs, outInfo);
}
/*
@@ -17857,22 +17879,43 @@
boolean deleteCodeAndResources, int[] allUserHandles, int flags,
PackageRemovedInfo outInfo, boolean writeSettings,
PackageParser.Package replacingPackage) {
- final DeletePackageAction action = mayDeletePackageLIF(packageName);
+ final DeletePackageAction action;
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
+ PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
+ action = mayDeletePackageLocked(outInfo, ps, disabledPs, children);
+ }
if (null == action) {
return false;
}
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
- return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
- allUserHandles, flags, outInfo, writeSettings, replacingPackage);
+ try {
+ executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
+ allUserHandles, flags, writeSettings, replacingPackage);
+ } catch (SystemDeleteException e) {
+ return false;
+ }
+ return true;
}
- private boolean executeDeletePackageLIF(DeletePackageAction action,
+ private static class SystemDeleteException extends Exception {
+ public final PackageManagerException reason;
+
+ private SystemDeleteException(PackageManagerException reason) {
+ this.reason = reason;
+ }
+ }
+
+ /** 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, PackageRemovedInfo outInfo,
- boolean writeSettings, PackageParser.Package replacingPackage) {
+ int[] allUserHandles, int flags, boolean writeSettings,
+ PackageParser.Package replacingPackage) throws SystemDeleteException {
final PackageSetting ps = action.deletingPs;
+ final PackageRemovedInfo outInfo = action.outInfo;
final boolean systemApp = isSystemApp(ps);
synchronized (mPackages) {
@@ -17888,7 +17931,7 @@
clearPackageStateForUserLIF(ps, removedUserId, outInfo);
markPackageUninstalledForUserLPw(ps, user);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
}
}
@@ -17917,7 +17960,7 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
} else {
// We need to set it back to 'installed' so the uninstall
// broadcasts will be sent correctly.
@@ -17933,7 +17976,7 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
}
}
@@ -17958,15 +18001,15 @@
}
// TODO(b/109941548): break reasons for ret = false out into mayDelete method
- final boolean ret;
if (systemApp) {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
- ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
+ deleteSystemPackageLIF(
+ action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
- ret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
+ deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
outInfo, writeSettings, replacingPackage);
}
@@ -18015,8 +18058,6 @@
}
}
}
-
- return ret;
}
@GuardedBy("mPackages")
@@ -18477,7 +18518,7 @@
@GuardedBy("mPackages")
private int getUidTargetSdkVersionLockedLPr(int uid) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getSettingLPr(uid);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -19742,7 +19783,7 @@
enableSystemPackageLPw(deletedPkg);
}
installPackageFromSystemLIF(deletedPkg.codePath,
- false /*isPrivileged*/, null /*allUserHandles*/,
+ /*isPrivileged*/ null /*allUserHandles*/,
null /*origUserHandles*/, null /*origPermissionsState*/,
true /*writeSettings*/);
} catch (PackageManagerException pme) {
@@ -20096,7 +20137,7 @@
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
? Zygote.MOUNT_EXTERNAL_FULL
: Zygote.MOUNT_EXTERNAL_WRITE;
@@ -21698,7 +21739,6 @@
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final PackageManager pm = mContext.getPackageManager();
- final boolean currentAsec;
final String currentVolumeUuid;
final File codeFile;
final String installerPackageName;
@@ -21732,22 +21772,13 @@
"3rd party apps are not allowed on internal storage");
}
- if (pkg.applicationInfo.isExternalAsec()) {
- currentAsec = true;
- currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
- } else if (pkg.applicationInfo.isForwardLocked()) {
- currentAsec = true;
- currentVolumeUuid = "forward_locked";
- } else {
- currentAsec = false;
- currentVolumeUuid = ps.volumeUuid;
+ currentVolumeUuid = ps.volumeUuid;
- final File probe = new File(pkg.codePath);
- final File probeOat = new File(probe, "oat");
- if (!probe.isDirectory() || !probeOat.isDirectory()) {
- throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
- "Move only supported for modern cluster style installs");
- }
+ final File probe = new File(pkg.codePath);
+ final File probeOat = new File(probe, "oat");
+ if (!probe.isDirectory() || !probeOat.isDirectory()) {
+ throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+ "Move only supported for modern cluster style installs");
}
if (Objects.equals(currentVolumeUuid, volumeUuid)) {
@@ -21784,12 +21815,11 @@
final boolean moveCompleteApp;
final File measurePath;
+ installFlags = INSTALL_INTERNAL;
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
- installFlags = INSTALL_INTERNAL;
- moveCompleteApp = !currentAsec;
+ moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
} else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
- installFlags = INSTALL_EXTERNAL;
moveCompleteApp = false;
measurePath = storage.getPrimaryPhysicalVolume().getPath();
} else {
@@ -21801,9 +21831,6 @@
"Move location not mounted private volume");
}
- Preconditions.checkState(!currentAsec);
-
- installFlags = INSTALL_INTERNAL;
moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
}
@@ -22588,7 +22615,7 @@
private SigningDetails getSigningDetails(int uid) {
synchronized (mPackages) {
final int appId = UserHandle.getAppId(uid);
- final Object obj = mSettings.getUserIdLPr(appId);
+ final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
return ((SharedUserSetting) obj).signatures.mSigningDetails;
@@ -23181,6 +23208,45 @@
throws IOException {
PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags);
}
+
+ @Override
+ public void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+ PackageManagerService.this.forEachPackage(actionLocked);
+ }
+
+ @Override
+ public ArraySet<String> getEnabledComponents(String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return new ArraySet<>();
+ }
+ return setting.getEnabledComponents(userId);
+ }
+ }
+
+ @Override
+ public ArraySet<String> getDisabledComponents(String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return new ArraySet<>();
+ }
+ return setting.getDisabledComponents(userId);
+ }
+ }
+
+ @Override
+ public @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ return setting.getEnabled(userId);
+ }
+ }
}
@GuardedBy("mPackages")
@@ -23303,6 +23369,15 @@
}
}
+ void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+ synchronized (mPackages) {
+ int numPackages = mPackages.size();
+ for (int i = 0; i < numPackages; i++) {
+ actionLocked.accept(mPackages.valueAt(i));
+ }
+ }
+ }
+
private static void enforceSystemOrPhoneCaller(String tag) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
@@ -23590,6 +23665,30 @@
return mProtectedPackages.isPackageStateProtected(userId, packageName);
}
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
+ "sendDeviceCustomizationReadyBroadcast");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
+ intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ final IActivityManager am = ActivityManager.getService();
+ final String[] requiredPermissions = {
+ Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
+ };
+ try {
+ am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions,
+ android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
static class ActiveInstallSession {
private final String mPackageName;
private final File mStagedDir;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6f275ec..77f8c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2193,9 +2193,6 @@
boolean replaceExisting = true;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "-l":
- sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- break;
case "-r": // ignore
break;
case "-R":
@@ -2210,9 +2207,6 @@
case "-t":
sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
break;
- case "-s":
- sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
- break;
case "-f":
sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
break;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b850613..2c2cc7e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,10 +152,6 @@
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
- public boolean isForwardLocked() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
-
public boolean isSystem() {
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 239dc99..fbf5439 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,7 +64,6 @@
| ApplicationInfo.PRIVATE_FLAG_VENDOR
| ApplicationInfo.PRIVATE_FLAG_PRODUCT
| ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
- | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
| ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6009bd3..c524dba 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -381,11 +381,9 @@
final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
new SparseArray<CrossProfileIntentResolver>();
- final ArrayMap<String, SharedUserSetting> mSharedUsers =
- new ArrayMap<String, SharedUserSetting>();
- private final ArrayList<Object> mUserIds = new ArrayList<Object>();
- private final SparseArray<Object> mOtherUserIds =
- new SparseArray<Object>();
+ final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
+ private final ArrayList<SettingBase> mAppIds = new ArrayList<>();
+ private final SparseArray<SettingBase> mOtherAppIds = new SparseArray<>();
// For reading/writing settings file.
private final ArrayList<Signature> mPastSignatures =
@@ -519,7 +517,7 @@
SharedUserSetting s = mSharedUsers.get(name);
if (s == null && create) {
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
- s.userId = newUserIdLPw(s);
+ s.userId = acquireAndRegisterNewAppIdLPw(s);
if (s.userId < 0) {
// < 0 means we couldn't assign a userid; throw exception
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
@@ -612,7 +610,7 @@
cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
p.appId = uid;
- if (addUserIdLPw(uid, p, name)) {
+ if (registerExistingAppIdLPw(uid, p, name)) {
mPackages.put(name, p);
return p;
}
@@ -635,7 +633,7 @@
}
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
- if (addUserIdLPw(uid, s, name)) {
+ if (registerExistingAppIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
@@ -885,13 +883,13 @@
* Registers a user ID with the system. Potentially allocates a new user ID.
* @throws PackageManagerException If a user ID could not be allocated.
*/
- void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
+ void registerAppIdLPw(PackageSetting p) throws PackageManagerException {
if (p.appId == 0) {
// Assign new user ID
- p.appId = newUserIdLPw(p);
+ p.appId = acquireAndRegisterNewAppIdLPw(p);
} else {
// Add new setting to list of user IDs
- addUserIdLPw(p.appId, p, p.name);
+ registerExistingAppIdLPw(p.appId, p, p.name);
}
if (p.appId < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -972,14 +970,14 @@
// If the we know about this user id, we have to update it as it
// has to point to the same PackageSetting instance as the package.
- Object userIdPs = getUserIdLPr(p.appId);
+ Object userIdPs = getSettingLPr(p.appId);
if (sharedUser == null) {
if (userIdPs != null && userIdPs != p) {
- replaceUserIdLPw(p.appId, p);
+ replaceAppIdLPw(p.appId, p);
}
} else {
if (userIdPs != null && userIdPs != sharedUser) {
- replaceUserIdLPw(p.appId, sharedUser);
+ replaceAppIdLPw(p.appId, sharedUser);
}
}
@@ -1083,11 +1081,11 @@
p.sharedUser.removePackage(p);
if (p.sharedUser.packages.size() == 0) {
mSharedUsers.remove(p.sharedUser.name);
- removeUserIdLPw(p.sharedUser.userId);
+ removeAppIdLPw(p.sharedUser.userId);
return p.sharedUser.userId;
}
} else {
- removeUserIdLPw(p.appId);
+ removeAppIdLPw(p.appId);
return p.appId;
}
}
@@ -1115,65 +1113,69 @@
mInstallerPackages.remove(packageName);
}
- private boolean addUserIdLPw(int uid, Object obj, Object name) {
- if (uid > Process.LAST_APPLICATION_UID) {
+ /** Returns true if the requested AppID was valid and not already registered. */
+ private boolean registerExistingAppIdLPw(int appId, SettingBase obj, Object name) {
+ if (appId > Process.LAST_APPLICATION_UID) {
return false;
}
- if (uid >= Process.FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- while (index >= N) {
- mUserIds.add(null);
- N++;
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ // fill the array until our index becomes valid
+ while (index >= size) {
+ mAppIds.add(null);
+ size++;
}
- if (mUserIds.get(index) != null) {
+ if (mAppIds.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate user id: " + uid
+ "Adding duplicate app id: " + appId
+ " name=" + name);
return false;
}
- mUserIds.set(index, obj);
+ mAppIds.set(index, obj);
} else {
- if (mOtherUserIds.get(uid) != null) {
+ if (mOtherAppIds.get(appId) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared id: " + uid
+ "Adding duplicate shared id: " + appId
+ " name=" + name);
return false;
}
- mOtherUserIds.put(uid, obj);
+ mOtherAppIds.put(appId, obj);
}
return true;
}
- public Object getUserIdLPr(int uid) {
- if (uid >= Process.FIRST_APPLICATION_UID) {
- final int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- return index < N ? mUserIds.get(index) : null;
+ /** Gets the setting associated with the provided App ID */
+ public SettingBase getSettingLPr(int appId) {
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ final int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ return index < size ? mAppIds.get(index) : null;
} else {
- return mOtherUserIds.get(uid);
+ return mOtherAppIds.get(appId);
}
}
- private void removeUserIdLPw(int uid) {
- if (uid >= Process.FIRST_APPLICATION_UID) {
- final int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, null);
+ /** Unregisters the provided app ID. */
+ void removeAppIdLPw(int appId) {
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ final int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ if (index < size) mAppIds.set(index, null);
} else {
- mOtherUserIds.remove(uid);
+ mOtherAppIds.remove(appId);
}
- setFirstAvailableUid(uid+1);
+ setFirstAvailableUid(appId + 1);
}
- private void replaceUserIdLPw(int uid, Object obj) {
- if (uid >= Process.FIRST_APPLICATION_UID) {
- final int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, obj);
+ private void replaceAppIdLPw(int appId, SettingBase obj) {
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ final int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ if (index < size) mAppIds.set(index, obj);
} else {
- mOtherUserIds.put(uid, obj);
+ mOtherAppIds.put(appId, obj);
}
}
@@ -3157,7 +3159,7 @@
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
final int sharedUserId = p.getSharedUserId();
- final Object idObj = getUserIdLPr(sharedUserId);
+ final Object idObj = getSettingLPr(sharedUserId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
p.sharedUser = sharedUser;
@@ -3202,7 +3204,7 @@
final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
while (disabledIt.hasNext()) {
final PackageSetting disabledPs = disabledIt.next();
- final Object id = getUserIdLPr(disabledPs.appId);
+ final Object id = getSettingLPr(disabledPs.appId);
if (id != null && id instanceof SharedUserSetting) {
disabledPs.sharedUser = (SharedUserSetting) id;
}
@@ -3656,7 +3658,6 @@
private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
- private static int PRE_M_APP_INFO_FLAG_FORWARD_LOCK = 1<<29;
private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -3756,15 +3757,11 @@
if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
}
- if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
- pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
- }
if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
| PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
- | PRE_M_APP_INFO_FLAG_FORWARD_LOCK
| PRE_M_APP_INFO_FLAG_PRIVILEGED);
} else {
// For backward compatibility
@@ -4206,24 +4203,24 @@
}
}
- // Returns -1 if we could not find an available UserId to assign
- private int newUserIdLPw(Object obj) {
+ /** Returns a new AppID or -1 if we could not find an available AppID to assign */
+ private int acquireAndRegisterNewAppIdLPw(SettingBase obj) {
// Let's be stupidly inefficient for now...
- final int N = mUserIds.size();
- for (int i = mFirstAvailableUid; i < N; i++) {
- if (mUserIds.get(i) == null) {
- mUserIds.set(i, obj);
+ final int size = mAppIds.size();
+ for (int i = mFirstAvailableUid; i < size; i++) {
+ if (mAppIds.get(i) == null) {
+ mAppIds.set(i, obj);
return Process.FIRST_APPLICATION_UID + i;
}
}
// None left?
- if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
+ if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
return -1;
}
- mUserIds.add(obj);
- return Process.FIRST_APPLICATION_UID + N;
+ mAppIds.add(obj);
+ return Process.FIRST_APPLICATION_UID + size;
}
public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() {
@@ -4258,11 +4255,48 @@
return false;
}
+ /**
+ * Returns the disabled {@link PackageSetting} for the provided package name if one exists,
+ * {@code null} otherwise.
+ */
+ @Nullable
public PackageSetting getDisabledSystemPkgLPr(String name) {
PackageSetting ps = mDisabledSysPackages.get(name);
return ps;
}
+ /**
+ * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting}
+ * if one exists, {@code null} otherwise.
+ */
+ @Nullable
+ public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) {
+ if (enabledPackageSetting == null) {
+ return null;
+ }
+ return getDisabledSystemPkgLPr(enabledPackageSetting.name);
+ }
+
+ /**
+ * Fetches an array of the child {@link PackageSetting}s for all child package names referenced
+ * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced.
+ *
+ * Note: Any child packages not found will be null in the returned array.
+ */
+ @Nullable
+ public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) {
+ if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) {
+ return null;
+ }
+ final int childCount = parentPackageSetting.childPackageNames.size();
+ PackageSetting[] children =
+ new PackageSetting[childCount];
+ for (int i = 0; i < childCount; i++) {
+ children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i));
+ }
+ return children;
+ }
+
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
final PackageSetting ps = mPackages.get(componentInfo.packageName);
if (ps == null) return false;
@@ -4428,7 +4462,6 @@
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
- ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
new file mode 100644
index 0000000..9e8b73e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import android.text.TextUtils;
+
+/**
+ * Represents a Share Target definition, read from the application's manifest (shortcuts.xml)
+ */
+class ShareTargetInfo {
+ static class TargetData {
+ final String mScheme;
+ final String mHost;
+ final String mPort;
+ final String mPath;
+ final String mPathPattern;
+ final String mPathPrefix;
+ final String mMimeType;
+
+ TargetData(String scheme, String host, String port, String path, String pathPattern,
+ String pathPrefix, String mimeType) {
+ mScheme = scheme;
+ mHost = host;
+ mPort = port;
+ mPath = path;
+ mPathPattern = pathPattern;
+ mPathPrefix = pathPrefix;
+ mMimeType = mimeType;
+ }
+
+ public void toStringInner(StringBuilder strBuilder) {
+ if (!TextUtils.isEmpty(mScheme)) {
+ strBuilder.append(" scheme=").append(mScheme);
+ }
+ if (!TextUtils.isEmpty(mHost)) {
+ strBuilder.append(" host=").append(mHost);
+ }
+ if (!TextUtils.isEmpty(mPort)) {
+ strBuilder.append(" port=").append(mPort);
+ }
+ if (!TextUtils.isEmpty(mPath)) {
+ strBuilder.append(" path=").append(mPath);
+ }
+ if (!TextUtils.isEmpty(mPathPattern)) {
+ strBuilder.append(" pathPattern=").append(mPathPattern);
+ }
+ if (!TextUtils.isEmpty(mPathPrefix)) {
+ strBuilder.append(" pathPrefix=").append(mPathPrefix);
+ }
+ if (!TextUtils.isEmpty(mMimeType)) {
+ strBuilder.append(" mimeType=").append(mMimeType);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ toStringInner(strBuilder);
+ return strBuilder.toString();
+ }
+ }
+
+ final TargetData[] mTargetData;
+ final String mTargetClass;
+ final String[] mCategories;
+
+ ShareTargetInfo(TargetData[] data, String targetClass, String[] categories) {
+ mTargetData = data;
+ mTargetClass = targetClass;
+ mCategories = categories;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ strBuilder.append("targetClass=").append(mTargetClass);
+ for (int i = 0; i < mTargetData.length; i++) {
+ strBuilder.append(" data={");
+ mTargetData[i].toStringInner(strBuilder);
+ strBuilder.append("}");
+ }
+ for (int i = 0; i < mCategories.length; i++) {
+ strBuilder.append(" category=").append(mCategories[i]);
+ }
+
+ return strBuilder.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 92e261a..83f0fde 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -111,6 +111,11 @@
final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
+ * All the share targets from the package
+ */
+ private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
+
+ /**
* # of times the package has called rate-limited APIs.
*/
private int mApiCallCount;
@@ -739,15 +744,16 @@
List<ShortcutInfo> newManifestShortcutList = null;
try {
newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
- getPackageName(), getPackageUserId());
+ getPackageName(), getPackageUserId(), mShareTargets);
} catch (IOException|XmlPullParserException e) {
Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
}
final int manifestShortcutSize = newManifestShortcutList == null ? 0
: newManifestShortcutList.size();
if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
- getPackageName(), manifestShortcutSize));
+ Slog.d(TAG,
+ String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
+ getPackageName(), manifestShortcutSize, mShareTargets.size()));
}
if (isNewApp && (manifestShortcutSize == 0)) {
// If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
@@ -1657,6 +1663,11 @@
return new ArrayList<>(mShortcuts.values());
}
+ @VisibleForTesting
+ List<ShareTargetInfo> getAllShareTargetsForTest() {
+ return new ArrayList<>(mShareTargets);
+ }
+
@Override
public void verifyStates() {
super.verifyStates();
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 866c46c..90f08c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
@@ -55,10 +56,14 @@
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_INTENT = "intent";
private static final String TAG_CATEGORIES = "categories";
+ private static final String TAG_SHARE_TARGET = "share-target";
+ private static final String TAG_DATA = "data";
+ private static final String TAG_CATEGORY = "category";
@Nullable
- public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
- String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+ public static List<ShortcutInfo> parseShortcuts(ShortcutService service, String packageName,
+ @UserIdInt int userId, @NonNull List<ShareTargetInfo> outShareTargets)
+ throws IOException, XmlPullParserException {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
packageName, userId));
@@ -69,6 +74,7 @@
}
List<ShortcutInfo> result = null;
+ outShareTargets.clear();
try {
final int size = activities.size();
@@ -82,8 +88,8 @@
service.getActivityInfoWithMetadata(
activityInfoNoMetadata.getComponentName(), userId);
if (activityInfoWithMetadata != null) {
- result = parseShortcutsOneFile(
- service, activityInfoWithMetadata, packageName, userId, result);
+ result = parseShortcutsOneFile(service, activityInfoWithMetadata, packageName,
+ userId, result, outShareTargets);
}
}
} catch (RuntimeException e) {
@@ -99,7 +105,8 @@
private static List<ShortcutInfo> parseShortcutsOneFile(
ShortcutService service,
ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
- List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+ List<ShortcutInfo> result, @NonNull List<ShareTargetInfo> outShareTargets)
+ throws IOException, XmlPullParserException {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format(
"Checking main activity %s", activityInfo.getComponentName()));
@@ -126,9 +133,19 @@
// after parsing <intent>. We keep the current one in here.
ShortcutInfo currentShortcut = null;
+ // We instantiate ShareTargetInfo at <share-target>, but add it to outShareTargets at
+ // </share-target>, after parsing <data> and <category>. We keep the current one here.
+ ShareTargetInfo currentShareTarget = null;
+
+ // Keeps parsed categories for both ShortcutInfo and ShareTargetInfo
Set<String> categories = null;
+
+ // Keeps parsed intents for ShortcutInfo
final ArrayList<Intent> intents = new ArrayList<>();
+ // Keeps parsed data fields for ShareTargetInfo
+ final ArrayList<ShareTargetInfo.TargetData> dataList = new ArrayList<>();
+
outer:
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
@@ -194,6 +211,32 @@
continue;
}
+ // When a share-target tag is closing, publish.
+ if ((type == XmlPullParser.END_TAG) && (depth == 2)
+ && (TAG_SHARE_TARGET.equals(tag))) {
+ if (currentShareTarget == null) {
+ // ShareTarget was invalid.
+ continue;
+ }
+ final ShareTargetInfo sti = currentShareTarget;
+ currentShareTarget = null; // Make sure to null out for the next iteration.
+
+ if (categories == null || categories.isEmpty() || dataList.isEmpty()) {
+ // Incomplete ShareTargetInfo.
+ continue;
+ }
+
+ final ShareTargetInfo newShareTarget = new ShareTargetInfo(
+ dataList.toArray(new ShareTargetInfo.TargetData[dataList.size()]),
+ sti.mTargetClass, categories.toArray(new String[categories.size()]));
+ outShareTargets.add(newShareTarget);
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "ShareTarget added: " + newShareTarget.toString());
+ }
+ categories = null;
+ dataList.clear();
+ }
+
// Otherwise, just look at start tags.
if (type != XmlPullParser.START_TAG) {
continue;
@@ -224,6 +267,17 @@
categories = null;
continue;
}
+ if (depth == 2 && TAG_SHARE_TARGET.equals(tag)) {
+ final ShareTargetInfo sti = parseShareTargetAttributes(service, attrs);
+ if (sti == null) {
+ // ShareTarget was invalid.
+ continue;
+ }
+ currentShareTarget = sti;
+ categories = null;
+ dataList.clear();
+ continue;
+ }
if (depth == 3 && TAG_INTENT.equals(tag)) {
if ((currentShortcut == null)
|| !currentShortcut.isEnabled()) {
@@ -258,6 +312,34 @@
categories.add(name);
continue;
}
+ if (depth == 3 && TAG_CATEGORY.equals(tag)) {
+ if ((currentShareTarget == null)) {
+ continue;
+ }
+ final String name = parseCategory(service, attrs);
+ if (TextUtils.isEmpty(name)) {
+ Log.e(TAG, "Empty category found. activity=" + activity);
+ continue;
+ }
+
+ if (categories == null) {
+ categories = new ArraySet<>();
+ }
+ categories.add(name);
+ continue;
+ }
+ if (depth == 3 && TAG_DATA.equals(tag)) {
+ if ((currentShareTarget == null)) {
+ continue;
+ }
+ final ShareTargetInfo.TargetData data = parseShareTargetData(service, attrs);
+ if (data == null) {
+ Log.e(TAG, "Invalid data tag found. activity=" + activity);
+ continue;
+ }
+ dataList.add(data);
+ continue;
+ }
Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
@@ -369,4 +451,57 @@
null, // bitmap path
disabledReason);
}
+
+ private static String parseCategory(ShortcutService service, AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.IntentCategory);
+ try {
+ if (sa.getType(R.styleable.IntentCategory_name) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:name must be string literal.");
+ return null;
+ }
+ return sa.getString(R.styleable.IntentCategory_name);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShareTargetInfo parseShareTargetAttributes(ShortcutService service,
+ AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.Intent);
+ try {
+ String targetClass = sa.getString(R.styleable.Intent_targetClass);
+ if (TextUtils.isEmpty(targetClass)) {
+ Log.w(TAG, "android:targetClass must be provided.");
+ return null;
+ }
+ return new ShareTargetInfo(null, targetClass, null);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShareTargetInfo.TargetData parseShareTargetData(ShortcutService service,
+ AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.AndroidManifestData);
+ try {
+ if (sa.getType(R.styleable.AndroidManifestData_mimeType) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:mimeType must be string literal.");
+ return null;
+ }
+ String scheme = sa.getString(R.styleable.AndroidManifestData_scheme);
+ String host = sa.getString(R.styleable.AndroidManifestData_host);
+ String port = sa.getString(R.styleable.AndroidManifestData_port);
+ String path = sa.getString(R.styleable.AndroidManifestData_path);
+ String pathPattern = sa.getString(R.styleable.AndroidManifestData_pathPattern);
+ String pathPrefix = sa.getString(R.styleable.AndroidManifestData_pathPrefix);
+ String mimeType = sa.getString(R.styleable.AndroidManifestData_mimeType);
+ return new ShareTargetInfo.TargetData(scheme, host, port, path, pathPattern, pathPrefix,
+ mimeType);
+ } finally {
+ sa.recycle();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index b9c3048..2b773f4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -41,8 +41,8 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -98,8 +98,8 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
import com.android.internal.util.StatLogger;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -1172,7 +1172,7 @@
return true;
}
}
-
+
// If the local copy says the user is locked, check with AM for the actual state, since
// the user might just have been unlocked.
// Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 505e4ee..1fd9b69 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -294,13 +294,14 @@
*/
public void detectLocaleChange() {
final String currentLocales = mService.injectGetLocaleTagsForUser(mUserId);
- if (getKnownLocales().equals(currentLocales)) {
+ if (!TextUtils.isEmpty(mKnownLocales) && mKnownLocales.equals(currentLocales)) {
return;
}
if (ShortcutService.DEBUG) {
- Slog.d(TAG, "Locale changed from " + currentLocales + " to " + mKnownLocales
+ Slog.d(TAG, "Locale changed from " + mKnownLocales + " to " + currentLocales
+ " for user " + mUserId);
}
+
mKnownLocales = currentLocales;
forAllPackages(pkg -> {
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 e194d15..2d583ca3 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -105,6 +105,8 @@
*/
private boolean perUser;
+ boolean usageInfoRequired;
+
public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
name = _name;
sourcePackageName = _sourcePackageName;
@@ -351,6 +353,7 @@
}
if (bp.perm == p) {
bp.protectionLevel = p.info.protectionLevel;
+ bp.usageInfoRequired = p.info.usageInfoRequired;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -430,6 +433,7 @@
permissionInfo.packageName = sourcePackageName;
permissionInfo.nonLocalizedLabel = name;
permissionInfo.protectionLevel = protectionLevel;
+ permissionInfo.usageInfoRequired = usageInfoRequired;
return permissionInfo;
}
@@ -458,6 +462,7 @@
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0;
if (dynamic) {
final PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -465,6 +470,7 @@
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
+ pi.usageInfoRequired = bp.usageInfoRequired;
bp.pendingPermissionInfo = pi;
}
out.put(bp.name, bp);
@@ -497,6 +503,7 @@
if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
serializer.attribute(null, "protection", Integer.toString(protectionLevel));
}
+ serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0");
if (type == BasePermission.TYPE_DYNAMIC) {
final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
if (pi != null) {
@@ -533,6 +540,7 @@
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -580,6 +588,8 @@
pw.print(" enforced=");
pw.println(readEnforced);
}
+ pw.print(" usageInfoRequired=");
+ pw.println(usageInfoRequired);
return true;
}
}
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 774134c..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;
@@ -144,6 +143,11 @@
LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
+ private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
+ static {
+ ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
+ }
+
private static final Set<String> COARSE_LOCATION_PERMISSIONS = new ArraySet<>();
static {
COARSE_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
@@ -184,7 +188,7 @@
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
@@ -193,20 +197,17 @@
private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
- MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_AUDIO);
}
}
private static final Set<String> MEDIA_VISUAL_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_IMAGES);
+ if (StorageManager.hasIsolatedStorage()) {
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_VIDEO);
+ MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
}
}
@@ -627,7 +628,7 @@
PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(packageName, userId,
- LOCATION_PERMISSIONS);
+ LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
}
}
@@ -1057,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/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index ec15c16..189d0f4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -181,4 +181,9 @@
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-}
\ No newline at end of file
+
+ /**
+ * Returns {@code true} if {@code permName} has {@code usageInfoRequired} set.
+ */
+ public abstract boolean isPermissionUsageInfoRequired(@NonNull String permName);
+}
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 c5d38db..4406fdd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2644,5 +2644,12 @@
return mSettings.getPermissionLocked(permName);
}
}
+ @Override
+ public boolean isPermissionUsageInfoRequired(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ BasePermission bp = mSettings.getPermissionLocked(permName);
+ return bp != null && bp.usageInfoRequired;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 79e2688..07bebad 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) {
@@ -241,12 +255,7 @@
// 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);
- }
+ mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
}
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
@@ -263,8 +272,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,
@@ -393,7 +408,7 @@
}
@Override
- public int getCurrentStatus() {
+ public int getCurrentThermalStatus() {
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -434,8 +449,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: " + Integer.toString(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 4124210..8ce3838 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CheckResult;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -30,21 +31,38 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.Signature;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.text.TextUtils;
import android.util.ArraySet;
+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.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;
@@ -96,7 +114,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);
@@ -108,19 +126,48 @@
@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) {
- //TODO only call into PermissionController if it or system upgreaded (for boot time)
- getUserStateLocked(userId);
+ userState = getUserStateLocked(userId);
}
- //TODO consider calling grants only when certain conditions are met
- // such as OS or PermissionController upgrade
- if (RemoteRoleControllerService.DEBUG) {
+ String packagesHash = computeComponentStateHash(userId);
+ String oldPackagesHash;
+ synchronized (mLock) {
+ oldPackagesHash = userState.getPackagesHashLocked();
+ }
+ 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(
@@ -129,7 +176,6 @@
public void onSuccess() {
result.complete(null);
}
-
@Override
public void onFailure() {
result.completeExceptionally(new RuntimeException());
@@ -137,19 +183,54 @@
});
try {
result.get(5, TimeUnit.SECONDS);
+ synchronized (mLock) {
+ userState.setPackagesHashLocked(packagesHash);
+ }
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
}
+ } else if (RemoteRoleControllerService.DEBUG) {
+ Slog.i(LOG_TAG, "Already ran grants for package state " + packagesHash);
}
}
+ @Nullable
+ private String computeComponentStateHash(@UserIdInt int userId) {
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ pm.forEachPackage(FunctionalUtils.uncheckExceptions(pkg -> {
+ out.write(pkg.packageName.getBytes());
+ out.write(BitUtils.toBytes(pkg.getLongVersionCode()));
+ out.write(pm.getApplicationEnabledState(pkg.packageName, userId));
+
+ ArraySet<String> enabledComponents =
+ pm.getEnabledComponents(pkg.packageName, userId);
+ int numComponents = CollectionUtils.size(enabledComponents);
+ for (int i = 0; i < numComponents; i++) {
+ out.write(enabledComponents.valueAt(i).getBytes());
+ }
+
+ ArraySet<String> disabledComponents =
+ pm.getDisabledComponents(pkg.packageName, userId);
+ numComponents = CollectionUtils.size(disabledComponents);
+ for (int i = 0; i < numComponents; i++) {
+ out.write(disabledComponents.valueAt(i).getBytes());
+ }
+ for (Signature signature : pkg.mSigningDetails.signatures) {
+ out.write(signature.toByteArray());
+ }
+ }));
+
+ 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();
+ userState = RoleUserState.newInstanceLocked(userId);
mUserStates.put(userId, userState);
}
return userState;
@@ -171,7 +252,7 @@
mControllerServices.remove(userId);
RoleUserState userState = mUserStates.removeReturnOld(userId);
if (userState != null) {
- userState.destroySyncLocked();
+ userState.destroyLocked();
}
}
}
@@ -334,5 +415,44 @@
return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false, true, name, null);
}
+
+ @Override
+ 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, " "));
+ }
+
+ synchronized (mLock) {
+ int[] userIds = mUserManagerInternal.getUserIds();
+ int userIdsLength = userIds.length;
+ for (int i = 0; i < userIdsLength; i++) {
+ int userId = userIds[i];
+
+ RoleUserState userState = getUserStateLocked(userId);
+ userState.dumpLocked(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
new file mode 100644
index 0000000..336b311
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.role;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.role.IRoleManager;
+import android.app.role.IRoleManagerCallback;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+class RoleManagerShellCommand extends ShellCommand {
+
+ @NonNull
+ private final IRoleManager mRoleManager;
+
+ RoleManagerShellCommand(@NonNull IRoleManager roleManager) {
+ mRoleManager = roleManager;
+ }
+
+ private class Callback extends IRoleManagerCallback.Stub {
+
+ @NonNull
+ private final CompletableFuture<Void> mResult = new CompletableFuture<>();
+
+ public int waitForResult() {
+ try {
+ mResult.get(5, TimeUnit.SECONDS);
+ return 0;
+ } catch (Exception e) {
+ getErrPrintWriter().println("Error: " + e.toString());
+ return -1;
+ }
+ }
+
+ @Override
+ public void onSuccess() {
+ mResult.complete(null);
+ }
+
+ @Override
+ public void onFailure() {
+ mResult.completeExceptionally(new RuntimeException("Failed"));
+ }
+ }
+
+ @Override
+ public int onCommand(@Nullable String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "add-role-holder":
+ return runAddRoleHolder();
+ case "remove-role-holder":
+ return runRemoveRoleHolder();
+ case "clear-role-holders":
+ return runClearRoleHolders();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int getUserIdMaybe() {
+ int userId = UserHandle.USER_SYSTEM;
+ String option = getNextOption();
+ if (option != null && option.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return userId;
+ }
+
+ private int runAddRoleHolder() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+ String packageName = getNextArgRequired();
+
+ Callback callback = new Callback();
+ mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback);
+ return callback.waitForResult();
+ }
+
+ private int runRemoveRoleHolder() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+ String packageName = getNextArgRequired();
+
+ Callback callback = new Callback();
+ mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback);
+ return callback.waitForResult();
+ }
+
+ private int runClearRoleHolders() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+
+ Callback callback = new Callback();
+ mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback);
+ return callback.waitForResult();
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Role manager (role) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE");
+ pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE");
+ pw.println(" clear-role-holders [--user USER_ID] ROLE");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 9c43f4d..327debf 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -31,6 +31,8 @@
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;
@@ -46,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.
@@ -58,31 +61,58 @@
private static final String ROLES_FILE_NAME = "roles.xml";
+ private static final long WRITE_DELAY_MILLIS = 200;
+ private static final long MAX_WRITE_DELAY_MILLIS = 2000;
+
private static final String TAG_ROLES = "roles";
private static final String TAG_ROLE = "role";
private static final String TAG_HOLDER = "holder";
private static final String ATTRIBUTE_VERSION = "version";
private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
@UserIdInt
private final int mUserId;
@GuardedBy("RoleManagerService.mLock")
- private int mVersion;
+ private int mVersion = VERSION_UNDEFINED;
+
+ @GuardedBy("RoleManagerService.mLock")
+ @Nullable
+ private String mPackagesHash;
/**
* Maps role names to its holders' package names. The values should never be null.
*/
@GuardedBy("RoleManagerService.mLock")
- private ArrayMap<String, ArraySet<String>> mRoles;
+ @NonNull
+ private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
+
+ @GuardedBy("RoleManagerService.mLock")
+ private long mWritePendingSinceMillis;
@GuardedBy("RoleManagerService.mLock")
private boolean mDestroyed;
+ @NonNull
private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
- public RoleUserState(@UserIdInt int userId) {
+ private RoleUserState(@UserIdInt int userId) {
mUserId = userId;
+
+ readLocked();
+ }
+
+ /**
+ * Create a new instance of user state, and read its state from disk if previously persisted.
+ *
+ * @param userId the user id for the new user state
+ *
+ * @return the new user state
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public static RoleUserState newInstanceLocked(@UserIdInt int userId) {
+ return new RoleUserState(userId);
}
/**
@@ -110,6 +140,31 @@
}
/**
+ * 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 getPackagesHashLocked() {
+ return mPackagesHash;
+ }
+
+ /**
+ * 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 setPackagesHashLocked(@Nullable String packagesHash) {
+ throwIfDestroyedLocked();
+ if (Objects.equals(mPackagesHash, packagesHash)) {
+ return;
+ }
+ mPackagesHash = packagesHash;
+ writeAsyncLocked();
+ }
+
+ /**
* Get whether the role is available.
*
* @param roleName the name of the role to get the holders for
@@ -229,22 +284,41 @@
@GuardedBy("RoleManagerService.mLock")
private void writeAsyncLocked() {
throwIfDestroyedLocked();
- int version = mVersion;
+
ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
- for (int i = 0, size = mRoles.size(); i < size; ++i) {
+ 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);
}
- mWriteHandler.removeCallbacksAndMessages(null);
- // TODO: Throttle writes.
- mWriteHandler.sendMessage(PooledLambda.obtainMessage(
- RoleUserState::writeSync, this, version, roles));
+
+ long currentTimeMillis = System.currentTimeMillis();
+ long writeDelayMillis;
+ if (!mWriteHandler.hasMessagesOrCallbacks()) {
+ mWritePendingSinceMillis = currentTimeMillis;
+ writeDelayMillis = WRITE_DELAY_MILLIS;
+ } else {
+ mWriteHandler.removeCallbacksAndMessages(null);
+ long writePendingDurationMillis = currentTimeMillis - mWritePendingSinceMillis;
+ if (writePendingDurationMillis >= MAX_WRITE_DELAY_MILLIS) {
+ writeDelayMillis = 0;
+ } else {
+ long maxWriteDelayMillis = Math.max(MAX_WRITE_DELAY_MILLIS
+ - writePendingDurationMillis, 0);
+ writeDelayMillis = Math.min(WRITE_DELAY_MILLIS, maxWriteDelayMillis);
+ }
+ }
+
+ mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
+ mVersion, mPackagesHash, roles), writeDelayMillis);
+ Slog.i(LOG_TAG, "Scheduled writing roles.xml");
}
@WorkerThread
- private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) {
+ private void writeSync(int version, @Nullable String packagesHash,
+ @NonNull ArrayMap<String, ArraySet<String>> roles) {
AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
FileOutputStream out = null;
try {
@@ -256,10 +330,11 @@
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
- serializeRoles(serializer, version, roles);
+ 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) {
@@ -272,17 +347,26 @@
@WorkerThread
private void serializeRoles(@NonNull XmlSerializer serializer, int version,
- @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException {
+ @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
+ throws IOException {
serializer.startTag(null, TAG_ROLES);
+
serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
+
+ 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);
}
@@ -291,6 +375,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);
@@ -301,20 +386,15 @@
* 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");
- }
-
+ private void readLocked() {
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");
- mRoles = new ArrayMap<>();
- mVersion = VERSION_UNDEFINED;
} catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
}
@@ -336,12 +416,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));
- mRoles = new ArrayMap<>();
+ mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
+ mRoles.clear();
int type;
int depth;
@@ -384,11 +466,47 @@
}
/**
+ * Dump this user state.
+ *
+ * @param dumpOutputStream the output stream to dump to
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpOutputStream,
+ @NonNull String fieldName, long fieldId) {
+ throwIfDestroyedLocked();
+
+ long fieldToken = dumpOutputStream.start(fieldName, fieldId);
+ dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
+ dumpOutputStream.write("version", RoleUserStateProto.VERSION, mVersion);
+ dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, mPackagesHash);
+
+ int rolesSize = mRoles.size();
+ for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
+ String roleName = mRoles.keyAt(rolesIndex);
+ ArraySet<String> roleHolders = mRoles.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);
+ }
+
+ /**
* 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.
*/
@GuardedBy("RoleManagerService.mLock")
- public void destroySyncLocked() {
+ public void destroyLocked() {
throwIfDestroyedLocked();
mWriteHandler.removeCallbacksAndMessages(null);
getFile(mUserId).delete();
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/services/core/java/com/android/server/signedconfig/InvalidConfigException.java
similarity index 62%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to services/core/java/com/android/server/signedconfig/InvalidConfigException.java
index 27d25b8..f01baa4 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/services/core/java/com/android/server/signedconfig/InvalidConfigException.java
@@ -13,12 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.server.signedconfig;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
+ * Thrown when there is a problem parsing the config embedded in an APK.
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+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/SignedConfig.java b/services/core/java/com/android/server/signedconfig/SignedConfig.java
new file mode 100644
index 0000000..a3f452c
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfig.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.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.
+ */
+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 = "minSdk";
+ private static final String CONFIG_KEY_MAX_SDK = "maxSdk";
+ private static final String CONFIG_KEY_VALUES = "values";
+ // TODO it may be better to use regular key/value pairs in a JSON object, rather than an array
+ // of objects with the 2 keys below.
+ private static final String CONFIG_KEY_KEY = "key";
+ private static final String CONFIG_KEY_VALUE = "value";
+
+ /**
+ * Represents config values targetting to 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 as read from the APK metadata.
+ * @return Parsed configuration.
+ * @throws InvalidConfigException If there's a problem parsing the config.
+ */
+ public static SignedConfig parse(String config, Set<String> allowedKeys)
+ 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));
+ }
+
+ return new SignedConfig(version, parsedConfigs);
+ } catch (JSONException e) {
+ throw new InvalidConfigException("Could not parse JSON", e);
+ }
+
+ }
+
+ @VisibleForTesting
+ static PerSdkConfig parsePerSdkConfig(JSONObject json, Set<String> allowedKeys)
+ throws JSONException, InvalidConfigException {
+ int minSdk = json.getInt(CONFIG_KEY_MIN_SDK);
+ int maxSdk = json.getInt(CONFIG_KEY_MAX_SDK);
+ JSONArray valueArray = json.getJSONArray(CONFIG_KEY_VALUES);
+ Map<String, String> values = new HashMap<>();
+ for (int i = 0; i < valueArray.length(); ++i) {
+ JSONObject keyValuePair = valueArray.getJSONObject(i);
+ String key = keyValuePair.getString(CONFIG_KEY_KEY);
+ String value = keyValuePair.has(CONFIG_KEY_VALUE)
+ ? keyValuePair.getString(CONFIG_KEY_VALUE)
+ : null;
+ if (!allowedKeys.contains(key)) {
+ throw new InvalidConfigException("Config key " + key + " is not allowed");
+ }
+ values.put(key, value);
+ }
+ return new PerSdkConfig(minSdk, maxSdk, values);
+ }
+
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
similarity index 69%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
index 27d25b8..7ce071f 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.server.signedconfig;
+
+import android.content.Context;
+
+class SignedConfigApplicator {
+
+ static void applyConfig(Context context, String config, String signature) {
+ //TODO verify signature
+ //TODO parse & apply config
+ }
+
}
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..1485686
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * 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);
+ if (DBG) {
+ Slog.d(TAG, "Got signed config: " + config);
+ Slog.d(TAG, "Got config signature: " + signature);
+ }
+ SignedConfigApplicator.applyConfig(mContext, 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/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 6f6846d..f0ebb75 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -51,6 +51,7 @@
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -198,6 +199,7 @@
"zygote64",
};
+ private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8;
static final class CompanionHandler extends Handler {
CompanionHandler(Looper looper) {
@@ -1077,7 +1079,7 @@
e.writeLong(processMemoryState.rssInBytes);
e.writeLong(processMemoryState.cacheInBytes);
e.writeLong(processMemoryState.swapInBytes);
- e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+ e.writeLong(0); // unused
e.writeLong(processMemoryState.startTimeNanos);
pulledData.add(e);
}
@@ -1086,6 +1088,7 @@
private void pullNativeProcessMemoryState(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
+ final List<String> processNames = Arrays.asList(MEMORY_INTERESTING_NATIVE_PROCESSES);
int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
for (int i = 0; i < pids.length; i++) {
int pid = pids[i];
@@ -1095,13 +1098,19 @@
}
int uid = getUidForPid(pid);
String processName = readCmdlineFromProcfs(pid);
+ // Sometimes we get here processName that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (!processNames.contains(processName)) {
+ continue;
+ }
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeString(processName);
e.writeLong(memoryStat.pgfault);
e.writeLong(memoryStat.pgmajfault);
e.writeLong(memoryStat.rssInBytes);
- e.writeLong(memoryStat.rssHighWatermarkInBytes);
+ e.writeLong(0); // unused
e.writeLong(memoryStat.startTimeNanos);
pulledData.add(e);
}
@@ -1515,6 +1524,21 @@
pulledData.add(e);
}
+ private void pullBuildInformation(int tagId,
+ long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(Build.FINGERPRINT);
+ e.writeString(Build.BRAND);
+ e.writeString(Build.PRODUCT);
+ e.writeString(Build.DEVICE);
+ e.writeString(Build.VERSION.RELEASE);
+ e.writeString(Build.ID);
+ e.writeString(Build.VERSION.INCREMENTAL);
+ e.writeString(Build.TYPE);
+ e.writeString(Build.TAGS);
+ pulledData.add(e);
+ }
+
private BatteryStatsHelper getBatteryStatsHelper() {
if (mBatteryStatsHelper == null) {
final long callingToken = Binder.clearCallingIdentity();
@@ -1636,6 +1660,11 @@
return;
}
int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
+ if (cpuFrequencies.length != CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES) {
+ Slog.w(TAG, "Expected " + CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES
+ + " frequencies, but got " + cpuFrequencies.length);
+ return;
+ }
for (int i = 0; i < processCpuUsages.size(); i++) {
KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -1649,23 +1678,18 @@
continue;
}
- for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) {
- // Do not report CPU usage at a frequency when it's zero
- if (threadCpuUsage.usageTimesMillis[k] == 0) {
- continue;
- }
-
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processCpuUsage.uid);
- e.writeInt(processCpuUsage.processId);
- e.writeInt(threadCpuUsage.threadId);
- e.writeString(processCpuUsage.processName);
- e.writeString(threadCpuUsage.threadName);
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processCpuUsage.uid);
+ e.writeInt(processCpuUsage.processId);
+ e.writeInt(threadCpuUsage.threadId);
+ e.writeString(processCpuUsage.processName);
+ e.writeString(threadCpuUsage.threadName);
+ for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES; k++) {
e.writeInt(cpuFrequencies[k]);
e.writeInt(threadCpuUsage.usageTimesMillis[k]);
- pulledData.add(e);
}
+ pulledData.add(e);
}
}
}
@@ -1808,6 +1832,10 @@
pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.BUILD_INFORMATION: {
+ pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.PROCESS_CPU_TIME: {
pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 1eb44a0..6e4c00e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -17,13 +17,17 @@
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;
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+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;
@@ -39,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;
@@ -62,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;
@@ -78,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);
@@ -184,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.
*/
@@ -239,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);
}
@@ -271,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) {}
}
}
@@ -373,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) {}
}
}
@@ -413,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) {}
}
}
@@ -448,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){
@@ -461,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
@@ -597,8 +586,8 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
enforceBiometricDialog();
if (mBar != null) {
try {
@@ -653,19 +642,33 @@
}
@Override
+ public void showBiometricTryAgain() {
+ enforceBiometricDialog();
+ if (mBar != null) {
+ try {
+ mBar.showBiometricTryAgain();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ // 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}.
@@ -677,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}.
@@ -688,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
@@ -711,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) {
}
}
@@ -796,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) {
@@ -829,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,
@@ -869,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;
}
}
@@ -927,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,
@@ -944,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);
}
}
@@ -1080,14 +1167,16 @@
}
@Override
- public void onNotificationActionClick(String key, int actionIndex, NotificationVisibility nv) {
+ public void onNotificationActionClick(
+ String key, int actionIndex, Notification.Action action, NotificationVisibility nv,
+ boolean generatedByAssistant) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
- actionIndex, nv);
+ actionIndex, action, nv, generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1177,12 +1266,14 @@
}
@Override
- public void onNotificationSmartReplySent(String key, int replyIndex)
+ public void onNotificationSmartReplySent(
+ String key, int replyIndex, CharSequence reply, boolean generatedByAssistant)
throws RemoteException {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+ mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
+ generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1293,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/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 6a0b648..9d6a647 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -16,6 +16,7 @@
package com.android.server.storage;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
@@ -25,8 +26,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnectorException;
import libcore.io.IoUtils;
-import java.io.File;
-import java.io.FileNotFoundException;
import java.util.concurrent.CountDownLatch;
/**
@@ -87,7 +86,7 @@
}
}
- public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode)
+ public ParcelFileDescriptor openFile(int mountId, int fileId, int mode)
throws FuseUnavailableMountException, InterruptedException {
final MountScope scope;
synchronized (this) {
@@ -96,17 +95,14 @@
throw new FuseUnavailableMountException(mountId);
}
}
- if (scope.pid != pid) {
- throw new SecurityException("PID does not match");
- }
final boolean result = scope.waitForMount();
if (result == false) {
throw new FuseUnavailableMountException(mountId);
}
try {
- return ParcelFileDescriptor.open(
- new File(scope.mountPoint, String.valueOf(fileId)), mode);
- } catch (FileNotFoundException error) {
+ int flags = FileUtils.translateModePfdToPosix(mode);
+ return scope.openFile(mountId, fileId, flags);
+ } catch (NativeDaemonConnectorException error) {
throw new FuseUnavailableMountException(mountId);
}
}
@@ -131,17 +127,13 @@
public static abstract class MountScope implements AutoCloseable {
public final int uid;
- public final int pid;
public final int mountId;
- public final File mountPoint;
private final CountDownLatch mMounted = new CountDownLatch(1);
private boolean mMountResult = false;
- public MountScope(int uid, int pid, int mountId) {
+ public MountScope(int uid, int mountId) {
this.uid = uid;
- this.pid = pid;
this.mountId = mountId;
- this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId));
}
@GuardedBy("AppFuseBridge.this")
@@ -159,6 +151,8 @@
}
public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
+ public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
+ throws NativeDaemonConnectorException;
}
private native long native_new();
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 8d27d1e..c8a68b4 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -402,7 +402,7 @@
throws RemoteException {
try {
final int uid = context.getPackageManager()
- .getPackageUid(packageName, 0);
+ .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
Preconditions.checkArgument(Binder.getCallingUid() == uid);
} catch (IllegalArgumentException | NullPointerException |
PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index da0a794..4b413e5 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -49,7 +49,7 @@
import libcore.icu.ICU;
import libcore.timezone.TzDataSetVersion;
import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d5e59c8..1163d39 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -626,7 +626,7 @@
updateServiceConnectionLocked(serviceState.component, userId);
}
- private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+ private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
@@ -638,6 +638,7 @@
// Set up a callback to send the session token.
ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
+ boolean created = true;
// Create a session. When failed, send a null token immediately.
try {
if (sessionState.isRecordingSession) {
@@ -647,11 +648,12 @@
}
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
- removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
null, sessionState.seq);
+ created = false;
}
channels[1].dispose();
+ return created;
}
private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
@@ -1193,8 +1195,10 @@
serviceState.sessionTokens.add(sessionToken);
if (serviceState.service != null) {
- createSessionInternalLocked(serviceState.service, sessionToken,
- resolvedUserId);
+ if (!createSessionInternalLocked(serviceState.service, sessionToken,
+ resolvedUserId)) {
+ removeSessionStateLocked(sessionToken, resolvedUserId);
+ }
} else {
updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
}
@@ -2282,9 +2286,17 @@
}
}
+ List<IBinder> tokensToBeRemoved = new ArrayList<>();
+
// And create sessions, if any.
for (IBinder sessionToken : serviceState.sessionTokens) {
- createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
+ if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
+ tokensToBeRemoved.add(sessionToken);
+ }
+ }
+
+ for (IBinder sessionToken : tokensToBeRemoved) {
+ removeSessionStateLocked(sessionToken, mUserId);
}
for (TvInputState inputState : userState.inputMap.values()) {
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
similarity index 62%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
index 27d25b8..7310af3 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.server.updates;
+
+public class ConversationActionsInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public ConversationActionsInstallReceiver() {
+ super(
+ "/data/misc/textclassifier/",
+ "actions_suggestions.model",
+ "metadata/actions_suggestions",
+ "version");
+ }
}
diff --git a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
index dfe02ec..05dad21 100644
--- a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
@@ -21,8 +21,8 @@
public LangIdInstallReceiver() {
super(
"/data/misc/textclassifier/",
- "textclassifier.langid.model",
- "metadata/langid",
+ "lang_id.model",
+ "metadata/lang_id",
"version");
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 409d2b4..64ff9cf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -88,7 +88,6 @@
import android.util.Xml;
import android.view.Display;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
@@ -487,6 +486,8 @@
private void generateCrop(WallpaperData wallpaper) {
boolean success = false;
+ // Only generate crop for default display.
+ final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
Rect cropHint = new Rect(wallpaper.cropHint);
if (DEBUG) {
@@ -494,7 +495,7 @@
+ Integer.toHexString(wallpaper.whichPending)
+ " to " + wallpaper.cropFile.getName()
+ " crop=(" + cropHint.width() + 'x' + cropHint.height()
- + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
+ + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
}
// Analyse the source; needed in multiple cases
@@ -533,11 +534,11 @@
}
// scale if the crop height winds up not matching the recommended metrics
- needScale = (wallpaper.height != cropHint.height());
+ needScale = (wpData.mHeight != cropHint.height());
if (DEBUG) {
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
- Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+ Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
}
@@ -567,7 +568,7 @@
// just let the decode take care of it because we also want to remap where the
// cropHint rectangle lies in the decoded [super]rect.
final BitmapFactory.Options scaler;
- final int actualScale = cropHint.height() / wallpaper.height;
+ final int actualScale = cropHint.height() / wpData.mHeight;
int scale = 1;
while (2*scale < actualScale) {
scale *= 2;
@@ -593,17 +594,18 @@
cropHint.offsetTo(0, 0);
cropHint.right /= scale; // adjust by downsampling factor
cropHint.bottom /= scale;
- final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+ final float heightR =
+ ((float) wpData.mHeight) / ((float) cropHint.height());
if (DEBUG) {
Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
}
final int destWidth = (int)(cropHint.width() * heightR);
final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
- destWidth, wallpaper.height, true);
+ destWidth, wpData.mHeight, true);
if (DEBUG) {
Slog.v(TAG, "Final extract:");
- Slog.v(TAG, " dims: w=" + wallpaper.width
- + " h=" + wallpaper.height);
+ Slog.v(TAG, " dims: w=" + wpData.mWidth
+ + " h=" + wpData.mHeight);
Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ " h=" + finalCrop.getHeight());
}
@@ -670,13 +672,13 @@
if (connector == null) return;
connector.disconnectLocked();
mLastWallpaper.connection.removeDisplayConnector(displayId);
+ mLastWallpaper.removeDisplayData(displayId);
}
}
}
@Override
public void onDisplayChanged(int displayId) {
- // TODO(b/115486823) Review that do we need to handle display changes.
}
};
@@ -778,16 +780,23 @@
private RemoteCallbackList<IWallpaperManagerCallback> callbacks
= new RemoteCallbackList<IWallpaperManagerCallback>();
- int width = -1;
- int height = -1;
+ private static final class DisplayData {
+ int mWidth = -1;
+ int mHeight = -1;
+ final Rect mPadding = new Rect(0, 0, 0, 0);
+ final int mDisplayId;
+
+ DisplayData(int displayId) {
+ mDisplayId = displayId;
+ }
+ }
+ private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
/**
* The crop hint supplied for displaying a subset of the source image
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
- final Rect padding = new Rect(0, 0, 0, 0);
-
WallpaperData(int userId, String inputFileName, String cropFileName) {
this.userId = userId;
final File wallpaperDir = getWallpaperDir(userId);
@@ -803,6 +812,44 @@
boolean sourceExists() {
return wallpaperFile.exists();
}
+
+ void removeDisplayData(int displayId) {
+ mDisplayDatas.remove(displayId);
+ }
+ }
+
+ private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
+ WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+ if (wpdData == null) {
+ wpdData = new WallpaperData.DisplayData(displayId);
+ ensureSaneWallpaperDisplaySize(wpdData, displayId);
+ data.mDisplayDatas.append(displayId, wpdData);
+ }
+ return wpdData;
+ }
+
+ private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
+ int displayId) {
+ // We always want to have some reasonable width hint.
+ final int baseSize = getMaximumSizeDimension(displayId);
+ if (wpdData.mWidth < baseSize) {
+ wpdData.mWidth = baseSize;
+ }
+ if (wpdData.mHeight < baseSize) {
+ wpdData.mHeight = baseSize;
+ }
+ }
+
+ private int getMaximumSizeDimension(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ return display.getMaximumSizeDimension();
+ }
+
+ void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
+ for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
+ final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+ action.accept(wpdData);
+ }
}
int makeWallpaperIdLocked() {
@@ -830,9 +877,11 @@
}
void ensureStatusHandled() {
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
+ mDisplayId);
if (mDimensionsChanged) {
try {
- mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+ mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set wallpaper dimensions", e);
}
@@ -840,7 +889,7 @@
}
if (mPaddingChanged) {
try {
- mEngine.setDisplayPadding(mWallpaper.padding);
+ mEngine.setDisplayPadding(wpdData.mPadding);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set wallpaper padding", e);
}
@@ -857,16 +906,16 @@
return;
}
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ mDisplayId);
try {
- // TODO(b/115486823) Consider the size of non-default display
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
- wallpaper.width, wallpaper.height,
- wallpaper.padding, mDisplayId);
+ wpdData.mWidth, wpdData.mHeight,
+ wpdData.mPadding, mDisplayId);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
- // TODO(b/115486823) Failed when attaching a new engine, however, other engines
- // may still working. Should we abandon them all or just ignore this one.
- if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+ if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+ && connection.getConnectedEngineSize() == 0) {
bindWallpaperComponentLocked(null /* componentName */, false /* force */,
false /* fromUser */, wallpaper, null /* reply */);
}
@@ -952,11 +1001,20 @@
void forEachDisplayConnector(Consumer<DisplayConnector> action) {
for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
- final DisplayConnector connector = mDisplayConnector.get(i);
+ final DisplayConnector connector = mDisplayConnector.valueAt(i);
action.accept(connector);
}
}
+ int getConnectedEngineSize() {
+ int engineSize = 0;
+ for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+ final DisplayConnector connector = mDisplayConnector.valueAt(i);
+ if (connector.mEngine != null) engineSize++;
+ }
+ return engineSize;
+ }
+
DisplayConnector getDisplayConnectorOrCreate(int displayId) {
DisplayConnector connector = mDisplayConnector.get(displayId);
if (connector == null) {
@@ -1123,12 +1181,13 @@
// TODO(multi-display) TBD.
if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
try {
- connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+ connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set ambient mode state", e);
}
}
- // TODO(b/115486823) Extends for secondary display.
+ // TODO(multi-display) So far, we have shared the same wallpaper on each display.
+ // Once we have multiple wallpapers on multiple displays, please complete here.
if (displayId == DEFAULT_DISPLAY) {
try {
// This will trigger onComputeColors in the wallpaper engine.
@@ -1560,7 +1619,7 @@
wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
final WallpaperData fallback = new WallpaperData(wallpaper.userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
- ensureSaneWallpaperData(fallback);
+ ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
mWaitingForUnlock = true;
}
@@ -1705,8 +1764,15 @@
return false;
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setDimensionHints(int width, int height, String callingPackage)
+ private boolean isValidDisplay(int displayId) {
+ return mDisplayManager.getDisplay(displayId) != null;
+ }
+
+ /**
+ * Sets the dimension hint for the wallpaper. These hints indicate the desired
+ * minimum width and height for the wallpaper in a particular display.
+ */
+ public void setDimensionHints(int width, int height, String callingPackage, int displayId)
throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
@@ -1719,90 +1785,113 @@
throw new IllegalArgumentException("width and height must be > 0");
}
- if (width != wallpaper.width || height != wallpaper.height) {
- wallpaper.width = width;
- wallpaper.height = height;
- saveSettingsLocked(userId);
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
+
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ if (width != wpdData.mWidth || height != wpdData.mHeight) {
+ wpdData.mWidth = width;
+ wpdData.mHeight = height;
+ if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- // TODO(b/115486823) Extends this method with specific display.
- final IWallpaperEngine engine = wallpaper.connection
- .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
if (engine != null) {
try {
engine.setDesiredSize(width, height);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
- } else if (wallpaper.connection.mService != null) {
+ } else if (wallpaper.connection.mService != null && connector != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- // TODO(b/115486823) Extends this method with specific display.
- wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
- .mDimensionsChanged = true;
+ connector.mDimensionsChanged = true;
}
}
}
}
}
- public int getWidthHint() throws RemoteException {
+ /**
+ * Returns the desired minimum width for the wallpaper in a particular display.
+ */
+ public int getWidthHint(int displayId) throws RemoteException {
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- return wallpaper.width;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ displayId);
+ return wpdData.mWidth;
} else {
return 0;
}
}
}
- public int getHeightHint() throws RemoteException {
+ /**
+ * Returns the desired minimum height for the wallpaper in a particular display.
+ */
+ public int getHeightHint(int displayId) throws RemoteException {
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- return wallpaper.height;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ displayId);
+ return wpdData.mHeight;
} else {
return 0;
}
}
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setDisplayPadding(Rect padding, String callingPackage) {
+ /**
+ * Sets extra padding that we would like the wallpaper to have outside of the display.
+ */
+ public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
return;
}
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
throw new IllegalArgumentException("padding must be positive: " + padding);
}
- if (!padding.equals(wallpaper.padding)) {
- wallpaper.padding.set(padding);
- saveSettingsLocked(userId);
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ if (!padding.equals(wpdData.mPadding)) {
+ wpdData.mPadding.set(padding);
+ if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- // TODO(b/115486823) Extends this method with specific display.
- final IWallpaperEngine engine = wallpaper.connection
- .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
if (engine != null) {
try {
engine.setDisplayPadding(padding);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
- } else if (wallpaper.connection.mService != null) {
+ } else if (wallpaper.connection.mService != null && connector != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- // TODO(b/115486823) Extends this method with specific display.
- wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
- .mPaddingChanged = true;
+ connector.mPaddingChanged = true;
}
}
}
@@ -1850,10 +1939,13 @@
// user switch)
return null;
}
+ // Only for default display.
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
try {
if (outParams != null) {
- outParams.putInt("width", wallpaper.width);
- outParams.putInt("height", wallpaper.height);
+ outParams.putInt("width", wpdData.mWidth);
+ outParams.putInt("height", wpdData.mHeight);
}
if (cb != null) {
wallpaper.callbacks.register(cb);
@@ -1931,11 +2023,17 @@
}
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
+ /**
+ * TODO(b/115486823) Extends this method with specific display.
+ * Propagate ambient state to wallpaper engine.
+ *
+ * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
+ * @param animationDuration Duration of the animation, or 0 when immediate.
+ */
+ public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
final IWallpaperEngine engine;
synchronized (mLock) {
- mInAmbientMode = inAmbienMode;
+ mInAmbientMode = inAmbientMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
if (data != null && data.connection != null && data.connection.mInfo != null
&& data.connection.mInfo.supportsAmbientMode()) {
@@ -1948,7 +2046,7 @@
if (engine != null) {
try {
- engine.setInAmbientMode(inAmbienMode, animated);
+ engine.setInAmbientMode(inAmbientMode, animationDuration);
} catch (RemoteException e) {
// Cannot talk to wallpaper engine.
}
@@ -2075,10 +2173,14 @@
// We know a-priori that there is no lock-only wallpaper currently
WallpaperData lockWP = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+ final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
+ DEFAULT_DISPLAY);
+ final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
+ DEFAULT_DISPLAY);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
- lockWP.width = sysWP.width;
- lockWP.height = sysWP.height;
+ lockWPDData.mWidth = sysWPDData.mWidth;
+ lockWPDData.mHeight = sysWPDData.mHeight;
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
@@ -2248,7 +2350,7 @@
return false;
}
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
- String msg = "Selected service does not require "
+ String msg = "Selected service does not have "
+ android.Manifest.permission.BIND_WALLPAPER
+ ": " + componentName;
if (fromUser) {
@@ -2300,6 +2402,22 @@
}
}
+ if (wi != null && wi.supportsAmbientMode()) {
+ final int hasPrivilege = mIPackageManager.checkPermission(
+ android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
+ serviceUserId);
+ if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Selected service does not have "
+ + android.Manifest.permission.AMBIENT_WALLPAPER
+ + ": " + componentName;
+ if (fromUser) {
+ throw new SecurityException(msg);
+ }
+ Slog.w(TAG, msg);
+ return false;
+ }
+ }
+
// Bind the service!
if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
@@ -2478,27 +2596,29 @@
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
- out.attribute(null, "width", Integer.toString(wallpaper.width));
- out.attribute(null, "height", Integer.toString(wallpaper.height));
+ out.attribute(null, "width", Integer.toString(wpdData.mWidth));
+ out.attribute(null, "height", Integer.toString(wpdData.mHeight));
out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
- if (wallpaper.padding.left != 0) {
- out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+ if (wpdData.mPadding.left != 0) {
+ out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
}
- if (wallpaper.padding.top != 0) {
- out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+ if (wpdData.mPadding.top != 0) {
+ out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
}
- if (wallpaper.padding.right != 0) {
- out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+ if (wpdData.mPadding.right != 0) {
+ out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
}
- if (wallpaper.padding.bottom != 0) {
- out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+ if (wpdData.mPadding.bottom != 0) {
+ out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
}
if (wallpaper.primaryColors != null) {
@@ -2601,14 +2721,14 @@
wallpaper = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
} else {
// sanity fallback: we're in bad shape, but establishing a known
// valid system+lock WallpaperData will keep us from dying.
Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
mWallpaperMap.put(userId, wallpaper);
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
}
}
}
@@ -2637,6 +2757,8 @@
}
}
boolean success = false;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
@@ -2663,8 +2785,8 @@
}
if (DEBUG) {
- Slog.v(TAG, "mWidth:" + wallpaper.width);
- Slog.v(TAG, "mHeight:" + wallpaper.height);
+ Slog.v(TAG, "mWidth:" + wpdData.mWidth);
+ Slog.v(TAG, "mHeight:" + wpdData.mHeight);
Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
Slog.v(TAG, "mName:" + wallpaper.name);
@@ -2700,10 +2822,8 @@
IoUtils.closeQuietly(stream);
if (!success) {
- wallpaper.width = -1;
- wallpaper.height = -1;
wallpaper.cropHint.set(0, 0, 0, 0);
- wallpaper.padding.set(0, 0, 0, 0);
+ wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
mLockWallpaperMap.remove(userId);
@@ -2717,26 +2837,23 @@
}
}
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
- ensureSaneWallpaperData(lockWallpaper);
+ ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
}
}
- private void ensureSaneWallpaperData(WallpaperData wallpaper) {
- // We always want to have some reasonable width hint.
- int baseSize = getMaximumSizeDimension();
- if (wallpaper.width < baseSize) {
- wallpaper.width = baseSize;
- }
- if (wallpaper.height < baseSize) {
- wallpaper.height = baseSize;
- }
- // and crop, if not previously specified
- if (wallpaper.cropHint.width() <= 0
- || wallpaper.cropHint.height() <= 0) {
- wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
+ private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
+ final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+
+ if (displayId == DEFAULT_DISPLAY) {
+ // crop, if not previously specified
+ if (wallpaper.cropHint.width() <= 0
+ || wallpaper.cropHint.height() <= 0) {
+ wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
+ }
}
}
@@ -2752,19 +2869,20 @@
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
+ final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+
if (!keepDimensionHints) {
- wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
- wallpaper.height = Integer.parseInt(parser
- .getAttributeValue(null, "height"));
+ wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+ wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
}
wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
- wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
- wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
- wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
- wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+ wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
+ wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
+ wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
+ wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
if (colorsCount > 0) {
Color primary = null, secondary = null, tertiary = null;
@@ -2787,12 +2905,6 @@
wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
}
- private int getMaximumSizeDimension() {
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- Display d = wm.getDefaultDisplay();
- return d.getMaximumSizeDimension();
- }
-
// Called by SystemBackupAgent after files are restored to disk.
public void settingsRestored() {
// Verify caller is the system
@@ -2832,7 +2944,7 @@
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ " id=" + wallpaper.wallpaperId);
if (success) {
- generateCrop(wallpaper); // based on the new image + metadata
+ generateCrop(wallpaper); // based on the new image + metadata
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
wallpaper, null);
}
@@ -2937,12 +3049,16 @@
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
pw.print(": id="); pw.println(wallpaper.wallpaperId);
- pw.print(" mWidth=");
- pw.print(wallpaper.width);
- pw.print(" mHeight=");
- pw.println(wallpaper.height);
+ forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(" displayId=");
+ pw.println(wpSize.mDisplayId);
+ pw.print(" mWidth=");
+ pw.print(wpSize.mWidth);
+ pw.print(" mHeight=");
+ pw.println(wpSize.mHeight);
+ pw.print(" mPadding="); pw.println(wpSize.mPadding);
+ });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
- pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
@@ -2973,11 +3089,15 @@
for (int i = 0; i < mLockWallpaperMap.size(); i++) {
WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
- pw.print(": id="); pw.println(wallpaper.wallpaperId);
- pw.print(" mWidth="); pw.print(wallpaper.width);
- pw.print(" mHeight="); pw.println(wallpaper.height);
+ pw.print(": id="); pw.println(wallpaper.wallpaperId);
+ forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(" displayId=");
+ pw.println(wpSize.mDisplayId);
+ pw.print(" mWidth="); pw.print(wpSize.mWidth);
+ pw.print(" mHeight="); pw.println(wpSize.mHeight);
+ pw.print(" mPadding="); pw.println(wpSize.mPadding);
+ });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
- pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e4d1cfe..fe0b5c2 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -706,7 +706,7 @@
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
.setName(SURFACE_TITLE)
- .setSize(mTempPoint.x, mTempPoint.y) // not a typo
+ .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
.build();
} catch (OutOfResourcesException oore) {
@@ -784,7 +784,7 @@
public void updateSize() {
synchronized (mService.mGlobalLock) {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+ mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
invalidate(mDirtyRect);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index ed36645..da997ba 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -39,8 +39,6 @@
import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
@@ -48,6 +46,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
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.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -84,7 +84,8 @@
*/
private static int sNextFreeStackId = 0;
- private ActivityStackSupervisor mSupervisor;
+ private ActivityTaskManagerService mService;
+ private RootActivityContainer mRootActivityContainer;
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
@@ -141,8 +142,9 @@
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
- ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
- mSupervisor = supervisor;
+ ActivityDisplay(RootActivityContainer root, Display display) {
+ mRootActivityContainer = root;
+ mService = root.mService;
mDisplayId = display.getDisplayId();
mDisplay = display;
mWindowContainerController = createWindowContainerController();
@@ -168,7 +170,7 @@
if (displayId != DEFAULT_DISPLAY) {
final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF && mOffToken == null) {
- mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId);
+ mOffToken = mService.acquireSleepToken("Display-off", displayId);
} else if (displayState == Display.STATE_ON && mOffToken != null) {
mOffToken.release();
mOffToken = null;
@@ -179,6 +181,11 @@
mWindowContainerController.onDisplayChanged();
}
+ @Override
+ public void onInitializeOverrideConfiguration(Configuration config) {
+ getRequestedOverrideConfiguration().updateFrom(config);
+ }
+
void addChild(ActivityStack stack, int position) {
if (position == POSITION_BOTTOM) {
position = 0;
@@ -189,7 +196,7 @@
+ " to displayId=" + mDisplayId + " position=" + position);
addStackReferenceIfNeeded(stack);
positionChildAt(stack, position);
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
}
void removeChild(ActivityStack stack) {
@@ -201,7 +208,7 @@
}
removeStackReferenceIfNeeded(stack);
releaseSelfIfNeeded();
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
onStackOrderChanged();
}
@@ -252,7 +259,7 @@
final ActivityStack currentFocusedStack = getFocusedStack();
if (currentFocusedStack != prevFocusedStack) {
mLastFocusedStack = prevFocusedStack;
- EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
+ EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId,
currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
updateLastFocusedStackReason);
@@ -409,10 +416,10 @@
}
}
- final ActivityTaskManagerService service = mSupervisor.mService;
- if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
- service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
- service.mSupportsPictureInPicture, activityType)) {
+ if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
+ mService.mSupportsSplitScreenMultiWindow,
+ mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
+ activityType)) {
throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ windowingMode);
}
@@ -425,10 +432,12 @@
<T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
if (windowingMode == WINDOWING_MODE_PINNED) {
- return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ return (T) new PinnedActivityStack(this, stackId,
+ mRootActivityContainer.mStackSupervisor, onTop);
}
- return (T) new ActivityStack(
- this, stackId, mSupervisor, windowingMode, activityType, onTop);
+ return (T) new ActivityStack(this, stackId,
+ mRootActivityContainer.mStackSupervisor, windowingMode, activityType,
+ onTop);
}
/**
@@ -543,7 +552,7 @@
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
// TODO(b/111541062): Check if resumed activity on this display instead
- if (!mSupervisor.isTopDisplayFocusedStack(stack)
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
&& stack.getResumedActivity() != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.getResumedActivity());
@@ -608,7 +617,7 @@
if (stack.getWindowingMode() != windowingMode) {
continue;
}
- mSupervisor.removeStack(stack);
+ mRootActivityContainer.mStackSupervisor.removeStack(stack);
}
}
}
@@ -623,7 +632,7 @@
for (int i = mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = mStacks.get(i);
if (stack.getActivityType() == activityType) {
- mSupervisor.removeStack(stack);
+ mRootActivityContainer.mStackSupervisor.removeStack(stack);
}
}
}
@@ -685,7 +694,7 @@
}
private void onSplitScreenModeDismissed() {
- mSupervisor.mWindowManager.deferSurfaceLayout();
+ mRootActivityContainer.mWindowManager.deferSurfaceLayout();
try {
// Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -709,12 +718,12 @@
mHomeStack.moveToFront("onSplitScreenModeDismissed");
topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
}
- mSupervisor.mWindowManager.continueSurfaceLayout();
+ mRootActivityContainer.mWindowManager.continueSurfaceLayout();
}
}
private void onSplitScreenModeActivated() {
- mSupervisor.mWindowManager.deferSurfaceLayout();
+ mRootActivityContainer.mWindowManager.deferSurfaceLayout();
try {
// Adjust the windowing mode of any affected by split-screen to split-screen secondary.
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -729,7 +738,7 @@
false /* creating */);
}
} finally {
- mSupervisor.mWindowManager.continueSurfaceLayout();
+ mRootActivityContainer.mWindowManager.continueSurfaceLayout();
}
}
@@ -824,11 +833,10 @@
int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
@Nullable TaskRecord task, int activityType) {
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- final ActivityTaskManagerService service = mSupervisor.mService;
- boolean supportsMultiWindow = service.mSupportsMultiWindow;
- boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
- boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
- boolean supportsPip = service.mSupportsPictureInPicture;
+ boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mService.mSupportsPictureInPicture;
if (supportsMultiWindow) {
if (task != null) {
supportsMultiWindow = task.isResizeable();
@@ -932,7 +940,7 @@
// This activity can be considered the top running activity if we are not considering
// the locked state, the keyguard isn't locked, or we can show when locked.
if (topRunning != null && considerKeyguardState
- && mSupervisor.getKeyguardController().isKeyguardLocked()
+ && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked()
&& !topRunning.canShowWhenLocked()) {
return null;
}
@@ -945,15 +953,16 @@
}
@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,
overrideConfiguration.windowConfiguration.getRotation());
}
- super.onOverrideConfigurationChanged(overrideConfiguration);
+ super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
}
@Override
@@ -1010,7 +1019,7 @@
@Override
protected ConfigurationContainer getParent() {
- return mSupervisor;
+ return mRootActivityContainer;
}
boolean isPrivate() {
@@ -1043,8 +1052,8 @@
// released (no more ActivityStack). But, we cannot release it at that moment or the
// related WindowContainer and WindowContainerController will also be removed. So, we
// set display as removed after reparenting stack finished.
- final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay();
- mSupervisor.beginDeferResume();
+ final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
+ mRootActivityContainer.mStackSupervisor.beginDeferResume();
try {
int numStacks = mStacks.size();
// Keep the order from bottom to top.
@@ -1070,7 +1079,7 @@
numStacks = mStacks.size();
}
} finally {
- mSupervisor.endDeferResume();
+ mRootActivityContainer.mStackSupervisor.endDeferResume();
}
mRemoved = true;
@@ -1082,9 +1091,9 @@
releaseSelfIfNeeded();
if (!mAllSleepTokens.isEmpty()) {
- mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
+ mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
mAllSleepTokens.clear();
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
}
}
@@ -1092,8 +1101,9 @@
if (mStacks.isEmpty() && mRemoved) {
mWindowContainerController.removeContainer();
mWindowContainerController = null;
- mSupervisor.removeChild(this);
- mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
+ mRootActivityContainer.removeChild(this);
+ mRootActivityContainer.mStackSupervisor
+ .getKeyguardController().onDisplayRemoved(mDisplayId);
}
}
@@ -1122,7 +1132,7 @@
boolean shouldSleep() {
return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mSupervisor.mService.mRunningVoice == null);
+ && (mService.mRunningVoice == null);
}
void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
@@ -1213,7 +1223,7 @@
@Nullable
ActivityRecord getHomeActivity() {
- return getHomeActivityForUser(mSupervisor.mCurrentUser);
+ return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
}
@Nullable
@@ -1233,7 +1243,7 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.isActivityTypeHome()
- && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+ && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
return r;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index e3133ef..eff0f75 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -42,7 +42,7 @@
* It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
* or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
*
- * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
* necessarily the same within a single launch sequence: it is only the top-most activity at the
* time (if any). Trampoline activities coalesce several activity starts into a single launch
* sequence.
@@ -94,6 +94,14 @@
public static final int TEMPERATURE_HOT = 3;
/**
+ * Typedef marker that a {@code byte[]} actually contains an
+ * <a href="proto/android/server/activitymanagerservice.proto">ActivityRecordProto</a>
+ * in the protobuf format.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ActivityRecordProto {}
+
+ /**
* Notifies the observer that a new launch sequence has begun as a result of a new intent.
*
* Once a launch sequence begins, the resolved activity will either subsequently start with
@@ -135,7 +143,7 @@
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
*/
- public void onActivityLaunched(@NonNull ActivityRecord activity,
+ public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
@Temperature int temperature);
/**
@@ -157,7 +165,7 @@
* in the case of a trampoline, multiple activities could've been started
* and only the latest activity is reported here.
*/
- public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+ public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] abortingActivity);
/**
* Notifies the observer that the current launch sequence has been successfully finished.
@@ -178,5 +186,5 @@
* and only the latest activity that was top-most during first-frame drawn
* is reported here.
*/
- public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
new file mode 100644
index 0000000..fa90dc5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+
+/**
+ * Multi-cast delegate implementation for {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br/><br/>
+ * This enables multiple launch observers to subscribe to {@link ActivityMetricsLogger}
+ * independently of each other.
+ *
+ * <br/><br/>
+ * Some callbacks in {@link ActivityMetricsLaunchObserver} have a {@code byte[]}
+ * parameter; this array is reused by all the registered observers, so it must not be written to
+ * (i.e. all observers must treat any array parameters as immutable).
+ *
+ * <br /><br />
+ * Multi-cast invocations occurs sequentially in-order of registered observers.
+ */
+public interface ActivityMetricsLaunchObserverRegistry {
+ /**
+ * Register an extra launch observer to receive the multi-cast.
+ *
+ * <br /><br />
+ * Multi-cast invocation happens in the same order the observers were registered. For example,
+ * <pre>
+ * registerLaunchObserver(A)
+ * registerLaunchObserver(B)
+ *
+ * obs.onIntentFailed() ->
+ * A.onIntentFailed()
+ * B.onIntentFailed()
+ * </pre>
+ */
+ void registerLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+
+ /**
+ * Unregister an existing launch observer. It will not receive the multi-cast in the future.
+ *
+ * <br /><br />
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1c08d03..12690a9 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -99,10 +99,12 @@
import android.util.SparseIntArray;
import android.util.StatsLog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
/**
@@ -168,7 +170,8 @@
* Due to the global single concurrent launch sequence, all calls to this observer must be made
* in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
*/
- private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+ private final LaunchObserverRegistryImpl mLaunchObserver;
+ @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
private final class H extends Handler {
@@ -251,7 +254,7 @@
type = getTransitionType(info);
processRecord = findProcessForActivity(launchedActivity);
processName = launchedActivity.processName;
- userId = launchedActivity.userId;
+ userId = launchedActivity.mUserId;
launchedActivityShortComponentName = launchedActivity.shortComponentName;
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
@@ -263,6 +266,7 @@
mSupervisor = supervisor;
mContext = context;
mHandler = new H(looper);
+ mLaunchObserver = new LaunchObserverRegistryImpl(looper);
}
void logWindowState() {
@@ -277,7 +281,8 @@
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ ActivityStack stack =
+ mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack();
if (stack == null) {
return;
}
@@ -289,7 +294,7 @@
@WindowingMode int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
- stack = mSupervisor.findStackBehind(stack);
+ stack = mSupervisor.mRootActivityContainer.findStackBehind(stack);
windowingMode = stack.getWindowingMode();
}
switch (windowingMode) {
@@ -515,7 +520,7 @@
if (info.launchedActivity != activityRecord) {
return;
}
- final TaskRecord t = activityRecord.getTask();
+ final TaskRecord t = activityRecord.getTaskRecord();
final SomeArgs args = SomeArgs.obtain();
args.arg1 = t;
args.arg2 = activityRecord;
@@ -850,9 +855,10 @@
builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
targetUidHasAnyVisibleWindow ? 1 : 0);
builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
- builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
- builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+ if (intent != null) {
+ builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+ }
if (callerApp != null) {
builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
@@ -881,29 +887,34 @@
(nowUptime - callerApp.getWhenUnimportant()));
}
}
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
- if (r.lastVisibleTime != 0) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
- (nowUptime - r.lastVisibleTime));
- }
- if (r.resultTo != null) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
- r.resultTo.shortComponentName);
- }
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
- r.visibleIgnoringKeyguard ? 1 : 0);
- if (r.lastLaunchTime != 0) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
- (nowUptime - r.lastLaunchTime));
+ if (r != null) {
+ builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY,
+ r.mActivityComponent.toShortString());
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
+ if (r.lastVisibleTime != 0) {
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
+ (nowUptime - r.lastVisibleTime));
+ }
+ if (r.resultTo != null) {
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME,
+ r.resultTo.packageName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
+ r.resultTo.shortComponentName);
+ }
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
+ r.visibleIgnoringKeyguard ? 1 : 0);
+ if (r.lastLaunchTime != 0) {
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
+ (nowUptime - r.lastLaunchTime));
+ }
}
mMetricsLogger.write(builder);
}
@@ -993,12 +1004,19 @@
}
}
+ public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+ return mLaunchObserver;
+ }
+
/** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
private void launchObserverNotifyIntentStarted(Intent intent) {
- if (mLaunchObserver != null) {
- // Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onIntentStarted(intent);
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyIntentStarted");
+
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onIntentStarted(intent);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1007,9 +1025,12 @@
* intent being delivered to the top running activity.
*/
private void launchObserverNotifyIntentFailed() {
- if (mLaunchObserver != null) {
- mLaunchObserver.onIntentFailed();
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyIntentFailed");
+
+ mLaunchObserver.onIntentFailed();
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1017,14 +1038,17 @@
* has started.
*/
private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunched");
+
@ActivityMetricsLaunchObserver.Temperature int temperature =
convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
- if (mLaunchObserver != null) {
- // Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onActivityLaunched(info.launchedActivity,
- temperature);
- }
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity),
+ temperature);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1032,11 +1056,15 @@
* cancelled.
*/
private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
- final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
- if (mLaunchObserver != null) {
- mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
- }
+ final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
+ info != null ? convertActivityRecordToProto(info.launchedActivity) : null;
+
+ mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1044,11 +1072,34 @@
* has fully finished (successfully).
*/
private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
- final ActivityRecord launchedActivity = info.launchedActivity;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- if (mLaunchObserver != null) {
- mLaunchObserver.onActivityLaunchFinished(launchedActivity);
- }
+ mLaunchObserver.onActivityLaunchFinished(
+ convertActivityRecordToProto(info.launchedActivity));
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @VisibleForTesting
+ static @ActivityMetricsLaunchObserver.ActivityRecordProto byte[]
+ convertActivityRecordToProto(ActivityRecord record) {
+ // May take non-negligible amount of time to convert ActivityRecord into a proto,
+ // so track the time.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:convertActivityRecordToProto");
+
+ // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream,
+ // so create a new one every time.
+ final ProtoOutputStream protoOutputStream =
+ new ProtoOutputStream(LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ // Write this data out as the top-most ActivityRecordProto (i.e. it is not a sub-object).
+ record.writeToProto(protoOutputStream);
+ final byte[] bytes = protoOutputStream.getBytes();
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return bytes;
}
private static @ActivityMetricsLaunchObserver.Temperature int
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5e92b9e..26a4cef 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,17 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -63,6 +73,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.res.Configuration.EMPTY;
@@ -111,12 +122,18 @@
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;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -147,6 +164,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
@@ -167,11 +185,14 @@
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.util.XmlUtils;
@@ -200,7 +221,7 @@
/**
* An entry in the history stack, representing an activity.
*/
-final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
+final class ActivityRecord extends ConfigurationContainer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
@@ -223,18 +244,20 @@
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
- final ActivityTaskManagerService service; // owner
+ final ActivityTaskManagerService mAtmService; // owner
final IApplicationToken.Stub appToken; // window manager token
- AppWindowContainerController mWindowContainerController;
+ // TODO: Remove after unification
+ AppWindowToken mAppWindowToken;
+
final ActivityInfo info; // all about me
// TODO: This is duplicated state already contained in info.applicationInfo - remove
ApplicationInfo appInfo; // information about activity's app
final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
- final int userId; // Which user is this running for?
+ final int mUserId; // Which user is this running for?
final Intent intent; // the original intent that generated us
- final ComponentName realActivity; // the intent component, or target of an alias.
+ final ComponentName mActivityComponent; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
final String packageName; // the package implementing intent's component
@@ -322,6 +345,7 @@
private boolean inHistory; // are we in the history stack?
final ActivityStackSupervisor mStackSupervisor;
+ final RootActivityContainer mRootActivityContainer;
static final int STARTING_WINDOW_NOT_SHOWN = 0;
static final int STARTING_WINDOW_SHOWN = 1;
@@ -383,14 +407,14 @@
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
- pw.print(" userId="); pw.println(userId);
+ pw.print(" userId="); pw.println(mUserId);
pw.print(prefix); pw.print("app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
pw.print(" task="); pw.println(task);
pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
- pw.print(prefix); pw.print("realActivity=");
- pw.println(realActivity.flattenToShortString());
+ pw.print(prefix); pw.print("mActivityComponent=");
+ pw.println(mActivityComponent.flattenToShortString());
if (appInfo != null) {
pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
@@ -417,8 +441,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());
@@ -620,7 +649,7 @@
"Reporting activity moved to display" + ", activityRecord=" + this
+ ", displayId=" + displayId + ", config=" + config);
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MoveToDisplayItem.obtain(displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -638,7 +667,7 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+ config);
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
ActivityConfigurationChangeItem.obtain(config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -665,7 +694,7 @@
private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
} catch (Exception e) {
// If process died, I don't care.
@@ -696,7 +725,7 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
overrideConfig));
} catch (Exception e) {
@@ -717,10 +746,10 @@
@Override
protected ConfigurationContainer getParent() {
- return getTask();
+ return getTaskRecord();
}
- TaskRecord getTask() {
+ TaskRecord getTaskRecord() {
return task;
}
@@ -741,12 +770,12 @@
* @param reparenting Whether we're in the middle of reparenting.
*/
void setTask(TaskRecord task, boolean reparenting) {
- // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
- if (task != null && task == getTask()) {
+ // Do nothing if the {@link TaskRecord} is the same as the current {@link getTaskRecord}.
+ if (task != null && task == getTaskRecord()) {
return;
}
- final ActivityStack oldStack = getStack();
+ final ActivityStack oldStack = getActivityStack();
final ActivityStack newStack = task != null ? task.getStack() : null;
// Inform old stack (if present) of activity removal and new stack (if set) of activity
@@ -769,10 +798,16 @@
}
/**
- * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
+ * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
+ * This information helps AWT know that the app is in the process of pausing before it gets the
+ * signal on the WM side.
*/
void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
- getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
+ if (mAppWindowToken == null) {
+ return;
+ }
+
+ mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip);
}
static class Token extends IApplicationToken.Stub {
@@ -789,7 +824,7 @@
return null;
}
ActivityRecord r = token.weakActivity.get();
- if (r == null || r.getStack() == null) {
+ if (r == null || r.getActivityStack() == null) {
return null;
}
return r;
@@ -822,7 +857,7 @@
}
boolean isResolverActivity() {
- return ResolverActivity.class.getName().equals(realActivity.getClassName());
+ return ResolverActivity.class.getName().equals(mActivityComponent.getClassName());
}
boolean isResolverOrChildActivity() {
@@ -831,7 +866,7 @@
}
try {
return ResolverActivity.class.isAssignableFrom(
- Object.class.getClassLoader().loadClass(realActivity.getClassName()));
+ Object.class.getClassLoader().loadClass(mActivityComponent.getClassName()));
} catch (ClassNotFoundException e) {
return false;
}
@@ -843,13 +878,14 @@
ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord) {
- service = _service;
+ mAtmService = _service;
+ mRootActivityContainer = _service.mRootActivityContainer;
appToken = new Token(this, _intent);
info = aInfo;
launchedFromPid = _launchedFromPid;
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
- userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
+ mUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
@@ -886,9 +922,9 @@
|| (aInfo.targetActivity.equals(_intent.getComponent().getClassName())
&& (aInfo.launchMode == LAUNCH_MULTIPLE
|| aInfo.launchMode == LAUNCH_SINGLE_TOP))) {
- realActivity = _intent.getComponent();
+ mActivityComponent = _intent.getComponent();
} else {
- realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity);
+ mActivityComponent = new ComponentName(aInfo.packageName, aInfo.targetActivity);
}
taskAffinity = aInfo.taskAffinity;
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
@@ -927,7 +963,7 @@
launchMode = aInfo.launchMode;
Entry ent = AttributeCache.instance().get(packageName,
- realTheme, com.android.internal.R.styleable.Window, userId);
+ realTheme, com.android.internal.R.styleable.Window, mUserId);
if (ent != null) {
fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
@@ -991,13 +1027,9 @@
return hasProcess() && app.hasThread();
}
- AppWindowContainerController getWindowContainerController() {
- return mWindowContainerController;
- }
-
- void createWindowContainer() {
- if (mWindowContainerController != null) {
- throw new IllegalArgumentException("Window container=" + mWindowContainerController
+ void createAppWindowToken() {
+ if (mAppWindowToken != null) {
+ throw new IllegalArgumentException("App Window Token=" + mAppWindowToken
+ " already created for r=" + this);
}
@@ -1010,12 +1042,31 @@
// Make sure override configuration is up-to-date before using to create window controller.
updateOverrideConfiguration();
- mWindowContainerController = new AppWindowContainerController(taskController, appToken,
- realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation,
- fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
- task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
- appInfo.targetSdkVersion, mRotationAnimationHint,
- ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+ // TODO: remove after unification
+ mAppWindowToken = mAtmService.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder());
+ if (mAppWindowToken != null) {
+ // TODO: Should this throw an exception instead?
+ Slog.w(TAG, "Attempted to add existing app token: " + appToken);
+ } else {
+ final Task container = taskController.mContainer;
+ if (container == null) {
+ throw new IllegalArgumentException("AppWindowContainerController: invalid "
+ + " controller=" + taskController);
+ }
+ mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,
+ task.voiceSession != null, container.getDisplayContent(),
+ ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
+ * 1000000L, fullscreen,
+ (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
+ info.screenOrientation, mRotationAnimationHint, info.configChanges,
+ mLaunchTaskBehind, isAlwaysFocusable());
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "addAppToken: "
+ + mAppWindowToken + " controller=" + taskController + " at "
+ + Integer.MAX_VALUE);
+ }
+ container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
+ }
task.addActivityToTop(this);
@@ -1026,17 +1077,51 @@
mLastReportedPictureInPictureMode = inPinnedWindowingMode();
}
+ boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+ CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+ IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "setAppStartingWindow: token=" + appToken
+ + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
+ + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
+ + " allowTaskSnapshot=" + allowTaskSnapshot);
+ }
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
+ return false;
+ }
+ return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
+ labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
+ processRunning, allowTaskSnapshot, activityCreated, fromRecents);
+ }
+
+ // TODO: Remove after unification
+ @VisibleForTesting
+ AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
+ boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
+ boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
+ int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+ boolean alwaysFocusable) {
+ return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc,
+ inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
+ rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+ this);
+ }
+
void removeWindowContainer() {
- // Do not try to remove a window container if we have already removed it.
- if (mWindowContainerController == null) {
+ if (mAtmService.mWindowManager.mRoot == null) return;
+
+ final DisplayContent dc = mAtmService.mWindowManager.mRoot.getDisplayContent(
+ getDisplayId());
+ if (dc == null) {
+ Slog.w(TAG, "removeWindowContainer: Attempted to remove token: "
+ + appToken + " from non-existing displayId=" + getDisplayId());
return;
}
-
// Resume key dispatching if it is currently paused before we remove the container.
resumeKeyDispatchingLocked();
-
- mWindowContainerController.removeContainer(getDisplayId());
- mWindowContainerController = null;
+ dc.removeAppToken(appToken.asBinder());
}
/**
@@ -1044,6 +1129,10 @@
* should ensure that the {@param newTask} is not already the parent of this activity.
*/
void reparent(TaskRecord newTask, int position, String reason) {
+ if (mAppWindowToken == null) {
+ Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
+ return;
+ }
final TaskRecord prevTask = task;
if (prevTask == newTask) {
throw new IllegalArgumentException(reason + ": task=" + newTask
@@ -1059,8 +1148,7 @@
+ " r=" + this + " (" + prevTask.getStackId() + ")");
}
- // Must reparent first in window manager
- mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+ mAppWindowToken.reparent(newTask.getWindowContainerController(), 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.
@@ -1110,7 +1198,7 @@
*/
private boolean canLaunchAssistActivity(String packageName) {
final ComponentName assistComponent =
- service.mActiveVoiceInteractionServiceComponent;
+ mAtmService.mActiveVoiceInteractionServiceComponent;
if (assistComponent != null) {
return assistComponent.getPackageName().equals(packageName);
}
@@ -1130,8 +1218,8 @@
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
- } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
- || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+ } else if (mActivityComponent.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
+ || mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent, appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
@@ -1149,16 +1237,16 @@
/**
* @return Stack value from current task, null if there is no task.
*/
- <T extends ActivityStack> T getStack() {
+ <T extends ActivityStack> T getActivityStack() {
return task != null ? (T) task.getStack() : null;
}
int getStackId() {
- return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
+ return getActivityStack() != null ? getActivityStack().mStackId : INVALID_STACK_ID;
}
ActivityDisplay getDisplay() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
return stack != null ? stack.getDisplay() : null;
}
@@ -1189,7 +1277,7 @@
}
boolean isInStackLocked() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
return stack != null && stack.isInStackLocked(this) != null;
}
@@ -1200,7 +1288,7 @@
}
boolean isFocusable() {
- return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
+ return mRootActivityContainer.isFocusable(this, isAlwaysFocusable());
}
boolean isResizeable() {
@@ -1219,7 +1307,7 @@
* @return whether this activity supports PiP multi-window and can be put in the pinned stack.
*/
boolean supportsPictureInPicture() {
- return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
+ return mAtmService.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
&& info.supportsPictureInPicture();
}
@@ -1232,7 +1320,7 @@
// An activity can not be docked even if it is considered resizeable because it only
// supports picture-in-picture mode but has a non-resizeable resizeMode
return super.supportsSplitScreenWindowingMode()
- && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ && mAtmService.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
}
/**
@@ -1240,16 +1328,16 @@
* stack.
*/
boolean supportsFreeform() {
- return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+ return mAtmService.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
}
/**
* @return whether this activity supports non-PiP multi-window.
*/
private boolean supportsResizeableMultiWindow() {
- return service.mSupportsMultiWindow && !isActivityTypeHome()
+ return mAtmService.mSupportsMultiWindow && !isActivityTypeHome()
&& (ActivityInfo.isResizeableMode(info.resizeMode)
- || service.mForceResizableActivities);
+ || mAtmService.mForceResizableActivities);
}
/**
@@ -1260,7 +1348,7 @@
* secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
- return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+ return mAtmService.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
launchedFromUid, info);
}
@@ -1281,13 +1369,13 @@
}
// Check to see if we are in VR mode, and disallow PiP if so
- if (service.shouldDisableNonVrUiLocked()) {
+ if (mAtmService.shouldDisableNonVrUiLocked()) {
return false;
}
- boolean isKeyguardLocked = service.isKeyguardLocked();
+ boolean isKeyguardLocked = mAtmService.isKeyguardLocked();
boolean isCurrentAppLocked =
- service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ mAtmService.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
final ActivityDisplay display = getDisplay();
boolean hasPinnedStack = display != null && display.hasPinnedStack();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
@@ -1328,7 +1416,7 @@
* @return Whether AppOps allows this package to enter picture-in-picture.
*/
private boolean checkEnterPictureInPictureAppOpsState() {
- return service.getAppOpsService().checkOperation(
+ return mAtmService.getAppOpsService().checkOperation(
OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
}
@@ -1345,15 +1433,15 @@
return false;
}
- final TaskRecord task = getTask();
- final ActivityStack stack = getStack();
+ final TaskRecord task = getTaskRecord();
+ final ActivityStack stack = getActivityStack();
if (stack == null) {
Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
+ this + " task=" + task);
return false;
}
- if (mStackSupervisor.getTopResumedActivity() == this) {
+ if (mRootActivityContainer.getTopResumedActivity() == this) {
if (DEBUG_FOCUS) {
Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
}
@@ -1366,9 +1454,9 @@
stack.moveToFront(reason, task);
// Report top activity change to tracking services and WM
- if (mStackSupervisor.getTopResumedActivity() == this) {
+ if (mRootActivityContainer.getTopResumedActivity() == this) {
// TODO(b/111361570): Support multiple focused apps in WM
- service.setResumedActivityUncheckLocked(this, reason);
+ mAtmService.setResumedActivityUncheckLocked(this, reason);
}
return true;
}
@@ -1378,7 +1466,7 @@
* {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
*/
boolean hasDismissKeyguardWindows() {
- return service.mWindowManager.containsDismissKeyguardWindow(appToken);
+ return mAtmService.mWindowManager.containsDismissKeyguardWindow(appToken);
}
void makeFinishingLocked() {
@@ -1390,14 +1478,14 @@
clearOptionsLocked();
}
- if (service != null) {
- service.getTaskChangeNotificationController().notifyTaskStackChanged();
+ if (mAtmService != null) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
}
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
- uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
+ uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this);
}
return uriPermissions;
}
@@ -1439,8 +1527,8 @@
}
final boolean isSleeping() {
- final ActivityStack stack = getStack();
- return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
+ final ActivityStack stack = getActivityStack();
+ return stack != null ? stack.shouldSleepActivities() : mAtmService.isSleepingLocked();
}
/**
@@ -1449,8 +1537,8 @@
*/
final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
// The activity now gets access to the data associated with this Intent.
- service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
- intent, getUriPermissionsLocked(), userId);
+ mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+ intent, getUriPermissionsLocked(), mUserId);
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
@@ -1464,7 +1552,7 @@
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
- service.getLifecycleManager().scheduleTransaction(
+ mAtmService.getLifecycleManager().scheduleTransaction(
app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
unsent = false;
} catch (RemoteException e) {
@@ -1490,7 +1578,7 @@
void applyOptionsLocked() {
if (pendingOptions != null
&& pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
- mWindowContainerController.applyOptionsLocked(pendingOptions, intent);
+ applyOptionsLocked(pendingOptions, intent);
if (task == null) {
clearOptionsLocked(false /* withAbort */);
} else {
@@ -1500,6 +1588,102 @@
}
}
+ /**
+ * Apply override app transition base on options & animation type.
+ */
+ void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+ final int animationType = pendingOptions.getAnimationType();
+ final DisplayContent displayContent = mAppWindowToken.getDisplayContent();
+ switch (animationType) {
+ case ANIM_CUSTOM:
+ displayContent.mAppTransition.overridePendingAppTransition(
+ pendingOptions.getPackageName(),
+ pendingOptions.getCustomEnterResId(),
+ pendingOptions.getCustomExitResId(),
+ pendingOptions.getOnAnimationStartListener());
+ break;
+ case ANIM_CLIP_REVEAL:
+ displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
+ break;
+ case ANIM_SCALE_UP:
+ displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
+ break;
+ case ANIM_THUMBNAIL_SCALE_UP:
+ case ANIM_THUMBNAIL_SCALE_DOWN:
+ final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+ final GraphicBuffer buffer = pendingOptions.getThumbnail();
+ displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getOnAnimationStartListener(),
+ scaleUp);
+ if (intent.getSourceBounds() == null && buffer != null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + buffer.getWidth(),
+ pendingOptions.getStartY() + buffer.getHeight()));
+ }
+ break;
+ case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
+ case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
+ final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+ final IAppTransitionAnimationSpecsFuture specsFuture =
+ pendingOptions.getSpecsFuture();
+ if (specsFuture != null) {
+ displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
+ specsFuture, pendingOptions.getOnAnimationStartListener(),
+ animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
+ } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+ && specs != null) {
+ displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
+ specs, pendingOptions.getOnAnimationStartListener(),
+ pendingOptions.getAnimationFinishedListener(), false);
+ } else {
+ displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
+ pendingOptions.getThumbnail(),
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight(),
+ pendingOptions.getOnAnimationStartListener(),
+ (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
+ }
+ break;
+ case ANIM_OPEN_CROSS_PROFILE_APPS:
+ displayContent.mAppTransition
+ .overridePendingAppTransitionStartCrossProfileApps();
+ break;
+ case ANIM_REMOTE_ANIMATION:
+ displayContent.mAppTransition.overridePendingAppTransitionRemote(
+ pendingOptions.getRemoteAnimationAdapter());
+ break;
+ case ANIM_NONE:
+ break;
+ default:
+ Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
+ break;
+ }
+ }
+
ActivityOptions getOptionsForTargetActivityLocked() {
return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
}
@@ -1532,8 +1716,11 @@
if (!keysPaused) {
keysPaused = true;
- if (mWindowContainerController != null) {
- mWindowContainerController.pauseKeyDispatching();
+ // TODO: remove the check after unification with AppWindowToken. The DC check is not
+ // needed after no mock mAppWindowToken in tests.
+ if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+ mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw(
+ mAppWindowToken);
}
}
}
@@ -1542,8 +1729,11 @@
if (keysPaused) {
keysPaused = false;
- if (mWindowContainerController != null) {
- mWindowContainerController.resumeKeyDispatching();
+ // TODO: remove the check after unification with AppWindowToken. The DC check is not
+ // needed after no mock mAppWindowToken in tests.
+ if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+ mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw(
+ mAppWindowToken);
}
}
}
@@ -1565,11 +1755,16 @@
}
void setVisibility(boolean visible) {
- mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+ + appToken);
+ return;
+ }
+ mAppWindowToken.setVisibility(visible, mDeferHidingClient);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
}
- // TODO: Look into merging with #setVisibility()
+ // TODO: Look into merging with #commitVisibility()
void setVisible(boolean newVisible) {
visible = newVisible;
mDeferHidingClient = !visible && mDeferHidingClient;
@@ -1589,7 +1784,7 @@
mState = state;
- final TaskRecord parent = getTask();
+ final TaskRecord parent = getTaskRecord();
if (parent != null) {
parent.onActivityStateChanged(this, state, reason);
@@ -1599,7 +1794,12 @@
// an indication that the Surface will eventually be destroyed.
// This however isn't necessarily true if we are going to sleep.
if (state == STOPPING && !isSleeping()) {
- mWindowContainerController.notifyAppStopping();
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+ + appToken);
+ return;
+ }
+ mAppWindowToken.detachChildren();
}
}
@@ -1637,7 +1837,12 @@
}
void notifyAppResumed(boolean wasStopped) {
- mWindowContainerController.notifyAppResumed(wasStopped);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
+ + appToken);
+ return;
+ }
+ mAppWindowToken.notifyAppResumed(wasStopped);
}
void notifyUnknownVisibilityLaunched() {
@@ -1645,7 +1850,10 @@
// No display activities never add a window, so there is no point in waiting them for
// relayout.
if (!noDisplay) {
- mWindowContainerController.notifyUnknownVisibilityLaunched();
+ if (mAppWindowToken != null) {
+ mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+ .notifyLaunched(mAppWindowToken);
+ }
}
}
@@ -1674,7 +1882,7 @@
// If this activity is paused, tell it to now show its window.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Making visible and scheduling visibility: " + this);
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
try {
if (stack.mTranslucentActivityWaiting != null) {
updateOptionsLocked(returningOptions);
@@ -1702,13 +1910,13 @@
void makeClientVisible() {
mClientVisibilityDeferred = false;
try {
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
if (shouldPauseWhenBecomingVisible()) {
// An activity must be in the {@link PAUSING} state for the system to validate
// the move to {@link PAUSED}.
setState(PAUSING, "makeVisibleIfNeeded");
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */));
}
@@ -1726,7 +1934,7 @@
// paused state. We also avoid doing this for the activity the stack supervisor
// considers the resumed activity, as normal means will bring the activity from STOPPED
// to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
- if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
+ if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
|| isResumedActivityOnDisplay()) {
return false;
}
@@ -1789,8 +1997,8 @@
if (isActivityTypeHome()) {
WindowProcessController app = task.mActivities.get(0).app;
- if (hasProcess() && app != service.mHomeProcess) {
- service.mHomeProcess = app;
+ if (hasProcess() && app != mAtmService.mHomeProcess) {
+ mAtmService.mHomeProcess = app;
}
}
@@ -1805,7 +2013,7 @@
mStackSupervisor.reportResumedActivityLocked(this);
resumeKeyDispatchingLocked();
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
mStackSupervisor.mNoAnimActivities.clear();
// Mark the point when the activity is resuming
@@ -1831,7 +2039,7 @@
final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
CharSequence description) {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (mState != STOPPING) {
Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
@@ -1839,7 +2047,7 @@
}
if (newPersistentState != null) {
persistentState = newPersistentState;
- service.notifyTaskPersisterLocked(task, false);
+ mAtmService.notifyTaskPersisterLocked(task, false);
}
if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle);
@@ -1857,16 +2065,18 @@
stopped = true;
setState(STOPPED, "activityStoppedLocked");
- mWindowContainerController.notifyAppStopped();
+ if (mAppWindowToken != null) {
+ mAppWindowToken.notifyAppStopped();
+ }
if (finishing) {
clearOptionsLocked();
} else {
if (deferRelaunchUntilPaused) {
stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
} else {
- mStackSupervisor.updatePreviousProcessLocked(this);
+ mRootActivityContainer.updatePreviousProcess(this);
}
}
}
@@ -1887,7 +2097,7 @@
return false;
}
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack == null) {
return false;
}
@@ -1900,7 +2110,7 @@
void finishLaunchTickingLocked() {
launchTickTime = 0;
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack != null) {
stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
}
@@ -1918,14 +2128,33 @@
public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
- mWindowContainerController.startFreezingScreen(configChanges);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM,
+ "Attempted to freeze screen with non-existing app token: " + appToken);
+ return;
+ }
+
+ if (configChanges == 0 && mAppWindowToken.okToDisplay()) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken);
+ return;
+ }
+
+ mAppWindowToken.startFreezingScreen();
}
}
public void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
- mWindowContainerController.stopFreezingScreen(force);
+ if (mAppWindowToken == null) {
+ return;
+ }
+ if (DEBUG_ORIENTATION) {
+ Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden="
+ + mAppWindowToken.isHidden() + " freezing="
+ + mAppWindowToken.isFreezingScreen());
+ }
+ mAppWindowToken.stopFreezingScreen(true, force);
}
}
@@ -1937,17 +2166,20 @@
info.windowsFullyDrawnDelayMs);
}
}
- @Override
+
+ /**
+ * Called when the starting window for this container is drawn.
+ */
public void onStartingWindowDrawn(long timestamp) {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
getWindowingMode(), timestamp);
}
}
- @Override
+ /** Called when the windows associated app window container are drawn. */
public void onWindowsDrawn(boolean drawn, long timestamp) {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
mDrawn = drawn;
if (!drawn) {
return;
@@ -1965,9 +2197,9 @@
}
}
- @Override
+ /** Called when the windows associated app window container are visible. */
public void onWindowsVisible() {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
mStackSupervisor.reportActivityVisibleLocked(this);
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
if (!nowVisible) {
@@ -1994,25 +2226,32 @@
mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
false /* remove */, true /* processPausingActivities */);
}
- service.scheduleAppGcsLocked();
+ mAtmService.scheduleAppGcsLocked();
}
}
}
- @Override
+ /** Called when the windows associated app window container are no longer visible. */
public void onWindowsGone() {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
nowVisible = false;
}
}
- @Override
+ /**
+ * Called when the key dispatching to a window associated with the app window container
+ * timed-out.
+ *
+ * @param reason The reason for the key dispatching time out.
+ * @param windowPid The pid of the window key dispatching timed out on.
+ * @return True if input dispatching should be aborted.
+ */
public boolean keyDispatchingTimedOut(String reason, int windowPid) {
ActivityRecord anrActivity;
WindowProcessController anrApp;
boolean windowFromSameProcessAsActivity;
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
anrActivity = getWaitingHistoryRecordLocked();
anrApp = app;
windowFromSameProcessAsActivity =
@@ -2020,13 +2259,13 @@
}
if (windowFromSameProcessAsActivity) {
- return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+ return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
app, false, reason);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
- return service.mAmInternal.inputDispatchingTimedOut(
+ return mAtmService.mAmInternal.inputDispatchingTimedOut(
windowPid, false /* aboveSystem */, reason) < 0;
}
}
@@ -2036,7 +2275,7 @@
// another activity to start or has stopped, then the key dispatching
// timeout should not be caused by this.
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
- final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
// Try to use the one which is closest to top.
ActivityRecord r = stack.getResumedActivity();
if (r == null) {
@@ -2053,14 +2292,14 @@
public boolean okToShowLocked() {
// We cannot show activities when the device is locked and the application is not
// encryption aware.
- if (!StorageManager.isUserKeyUnlocked(userId)
+ if (!StorageManager.isUserKeyUnlocked(mUserId)
&& !info.applicationInfo.isEncryptionAware()) {
return false;
}
return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
- || (mStackSupervisor.isCurrentProfileLocked(userId)
- && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
+ || (mStackSupervisor.isCurrentProfileLocked(mUserId)
+ && mAtmService.mAmInternal.isUserRunning(mUserId, 0 /* flags */));
}
/**
@@ -2107,13 +2346,13 @@
static ActivityRecord isInStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- return (r != null) ? r.getStack().isInStackLocked(r) : null;
+ return (r != null) ? r.getActivityStack().isInStackLocked(r) : null;
}
static ActivityStack getStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- return r.getStack();
+ return r.getActivityStack();
}
return null;
}
@@ -2123,7 +2362,7 @@
* {@link android.view.Display#INVALID_DISPLAY} if not attached.
*/
int getDisplayId() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack == null) {
return INVALID_DISPLAY;
}
@@ -2135,7 +2374,7 @@
// This would be redundant.
return false;
}
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity
|| !haveState || !stopped) {
// We're not ready for this kind of thing.
@@ -2161,7 +2400,7 @@
final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
iconFilename);
final String iconFilePath = iconFile.getAbsolutePath();
- service.getRecentTasks().saveImage(icon, iconFilePath);
+ mAtmService.getRecentTasks().saveImage(icon, iconFilePath);
_taskDescription.setIconFilename(iconFilePath);
}
taskDescription = _taskDescription;
@@ -2183,7 +2422,7 @@
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
boolean fromRecents) {
- if (mWindowContainerController == null) {
+ if (mAppWindowToken == null) {
return;
}
if (mTaskOverlay) {
@@ -2197,8 +2436,8 @@
}
final CompatibilityInfo compatInfo =
- service.compatibilityInfoForPackageLocked(info.applicationInfo);
- final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+ mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);
+ final boolean shown = addStartingWindow(packageName, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
allowTaskSnapshot(),
@@ -2213,34 +2452,62 @@
if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
mStartingWindowState = STARTING_WINDOW_REMOVED;
- mWindowContainerController.removeStartingWindow();
+ mAppWindowToken.removeStartingWindow();
}
}
int getRequestedOrientation() {
- return mWindowContainerController.getOrientation();
+ return getOrientation();
}
void setRequestedOrientation(int requestedOrientation) {
final int displayId = getDisplayId();
final Configuration displayConfig =
- mStackSupervisor.getDisplayOverrideConfiguration(displayId);
+ mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
- final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
+ final Configuration config = setOrientation(requestedOrientation,
displayId, displayConfig, mayFreezeScreenLocked(app));
if (config != null) {
frozenBeforeDestroy = true;
- if (!service.updateDisplayOverrideConfigurationLocked(config, this,
+ if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
false /* deferResume */, displayId)) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
- service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+ mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
task.taskId, requestedOrientation);
}
+ Configuration setOrientation(int requestedOrientation, int displayId,
+ Configuration displayConfig, boolean freezeScreenIfNeeded) {
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM,
+ "Attempted to set orientation of non-existing app token: " + appToken);
+ return null;
+ }
+
+ mAppWindowToken.setOrientation(requestedOrientation);
+
+ final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+ return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
+ displayId);
+ }
+
+ int getOrientation() {
+ if (mAppWindowToken == null) {
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ return mAppWindowToken.getOrientationIgnoreVisibility();
+ }
+
void setDisablePreviewScreenshots(boolean disable) {
- mWindowContainerController.setDisablePreviewScreenshots(disable);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
+ + " token: " + appToken);
+ return;
+ }
+ mAppWindowToken.setDisablePreviewScreenshots(disable);
}
/**
@@ -2269,27 +2536,27 @@
mTmpConfig.unset();
computeBounds(mTmpBounds);
- if (mTmpBounds.equals(getOverrideBounds())) {
+ if (mTmpBounds.equals(getRequestedOverrideBounds())) {
return;
}
setBounds(mTmpBounds);
- final Rect updatedBounds = getOverrideBounds();
+ final Rect updatedBounds = getRequestedOverrideBounds();
// Bounds changed...update configuration to match.
if (!matchParentBounds()) {
- task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
+ task.computeOverrideConfiguration(mTmpConfig, updatedBounds,
false /* overrideWidth */, false /* overrideHeight */);
}
- onOverrideConfigurationChanged(mTmpConfig);
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
}
/** Returns true if the configuration is compatible with this activity. */
boolean isConfigurationCompatible(Configuration config) {
- final int orientation = mWindowContainerController != null
- ? mWindowContainerController.getOrientation() : info.screenOrientation;
+ final int orientation = mAppWindowToken != null
+ ? getOrientation() : info.screenOrientation;
if (isFixedOrientationPortrait(orientation)
&& config.orientation != ORIENTATION_PORTRAIT) {
return false;
@@ -2308,7 +2575,7 @@
private void computeBounds(Rect outBounds) {
outBounds.setEmpty();
final float maxAspectRatio = info.maxAspectRatio;
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
|| isInVrUiMode(getConfiguration())) {
// We don't set override configuration if that activity task isn't fullscreen. I.e. the
@@ -2340,11 +2607,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;
}
@@ -2354,7 +2621,7 @@
// bounds would end up too small.
outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
- if (service.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
+ if (mAtmService.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
// Position the activity frame on the opposite side of the nav bar.
outBounds.left = appBounds.right - maxActivityWidth;
outBounds.right = appBounds.right;
@@ -2390,7 +2657,7 @@
*/
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreStopState) {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + this);
@@ -2453,7 +2720,7 @@
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+ setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -2507,7 +2774,7 @@
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
if (hasResizeChange) {
final boolean isDragResizing =
- getTask().getWindowContainerController().isDragResizing();
+ getTaskRecord().getWindowContainerController().isDragResizing();
mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
: RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
} else {
@@ -2640,7 +2907,7 @@
}
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
- if (service.mSuppressResizeConfigChanges && preserveWindow) {
+ if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) {
configChangeFlags = 0;
return;
}
@@ -2656,7 +2923,7 @@
+ " newIntents=" + pendingNewIntents + " andResume=" + andResume
+ " preserveWindow=" + preserveWindow);
EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY
- : AM_RELAUNCH_ACTIVITY, userId, System.identityHashCode(this),
+ : AM_RELAUNCH_ACTIVITY, mUserId, System.identityHashCode(this),
task.taskId, shortComponentName);
startFreezingScreenLocked(app, 0);
@@ -2669,7 +2936,7 @@
mStackSupervisor.activityRelaunchingLocked(this);
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(service.getGlobalConfiguration(),
+ new MergedConfiguration(mAtmService.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
@@ -2682,7 +2949,7 @@
final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
- service.getLifecycleManager().scheduleTransaction(transaction);
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
// sleeping.
@@ -2696,9 +2963,9 @@
}
results = null;
newIntents = null;
- service.getAppWarningsLocked().onResumeActivity(this);
+ mAtmService.getAppWarningsLocked().onResumeActivity(this);
} else {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack != null) {
stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
}
@@ -2713,7 +2980,7 @@
private boolean isProcessRunning() {
WindowProcessController proc = app;
if (proc == null) {
- proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
+ proc = mAtmService.mProcessNames.get(processName, info.applicationInfo.uid);
}
return proc != null && proc.hasThread();
}
@@ -2757,7 +3024,7 @@
out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
}
out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
- out.attribute(null, ATTR_USERID, String.valueOf(userId));
+ out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
if (taskDescription != null) {
taskDescription.saveToXml(out);
@@ -2867,7 +3134,7 @@
void setShowWhenLocked(boolean showWhenLocked) {
mShowWhenLocked = showWhenLocked;
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
+ mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */,
false /* preserveWindows */);
}
@@ -2880,7 +3147,7 @@
*/
boolean canShowWhenLocked() {
return !inPinnedWindowingMode() && (mShowWhenLocked
- || service.mWindowManager.containsShowWhenLockedWindow(appToken));
+ || mAtmService.mWindowManager.containsShowWhenLockedWindow(appToken));
}
void setTurnScreenOn(boolean turnScreenOn) {
@@ -2895,7 +3162,7 @@
* @return true if the screen can be turned on, false otherwise.
*/
boolean canTurnScreenOn() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
return mTurnScreenOn && stack != null &&
stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
}
@@ -2905,7 +3172,7 @@
}
boolean isTopRunningActivity() {
- return mStackSupervisor.topRunningActivityLocked() == this;
+ return mRootActivityContainer.topRunningActivity() == this;
}
/**
@@ -2918,7 +3185,12 @@
}
void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- mWindowContainerController.registerRemoteAnimations(definition);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+ + " token: " + appToken);
+ return;
+ }
+ mAppWindowToken.registerRemoteAnimations(definition);
}
@Override
@@ -2931,7 +3203,7 @@
sb.append("ActivityRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" u");
- sb.append(userId);
+ sb.append(mUserId);
sb.append(' ');
sb.append(intent.getComponent().flattenToShortString());
stringName = sb.toString();
@@ -2941,13 +3213,16 @@
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
- proto.write(USER_ID, userId);
+ proto.write(USER_ID, mUserId);
proto.write(TITLE, intent.getComponent().flattenToShortString());
proto.end(token);
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
+ /**
+ * Write all fields to an {@code ActivityRecordProto}. This assumes the
+ * {@code ActivityRecordProto} is the outer-most proto data.
+ */
+ void writeToProto(ProtoOutputStream proto) {
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
@@ -2957,6 +3232,11 @@
proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !fullscreen);
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ writeToProto(proto);
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bd3e43c..c41a173 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -63,7 +63,6 @@
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -103,6 +102,7 @@
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.RootActivityContainer.FindTaskResult;
import static java.lang.Integer.MAX_VALUE;
@@ -264,10 +264,10 @@
// 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);
}
- mStackSupervisor.updateUIDsPresentOnDisplay();
+ mRootActivityContainer.updateUIDsPresentOnDisplay();
}
enum ActivityState {
@@ -390,6 +390,7 @@
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
+ protected final RootActivityContainer mRootActivityContainer;
private boolean mTopActivityOccludesKeyguard;
private ActivityRecord mTopDismissingKeyguardActivity;
@@ -489,6 +490,7 @@
int windowingMode, int activityType, boolean onTop) {
mStackSupervisor = supervisor;
mService = supervisor.mService;
+ mRootActivityContainer = mService.mRootActivityContainer;
mHandler = new ActivityStackHandler(supervisor.mLooper);
mWindowManager = mService.mWindowManager;
mStackId = stackId;
@@ -508,7 +510,7 @@
T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
- mStackSupervisor.mWindowManager);
+ mRootActivityContainer.mWindowManager);
}
T getWindowContainerController() {
@@ -532,11 +534,11 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+ reason);
setResumedActivity(record, reason + " - onActivityStateChanged");
- if (record == mStackSupervisor.getTopResumedActivity()) {
+ if (record == mRootActivityContainer.getTopResumedActivity()) {
// TODO(b/111361570): Support multiple focused apps in WM
mService.setResumedActivityUncheckLocked(record, reason);
}
- mStackSupervisor.mRecentTasks.add(record.getTask());
+ mStackSupervisor.mRecentTasks.add(record.getTaskRecord());
}
}
@@ -560,7 +562,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
@@ -587,8 +589,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
@@ -600,11 +602,12 @@
}
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) {
+ } else if (
+ getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
final boolean isMinimizedDock = getDisplay().getWindowContainerController()
.mContainer.getDockedDividerController().isMinimizedDock();
@@ -622,7 +625,7 @@
display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- mStackSupervisor.resizeStackLocked(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
+ mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, true /* deferResume */);
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -670,7 +673,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();
@@ -723,7 +726,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;
}
@@ -789,7 +792,7 @@
mWindowContainerController.getRawBounds(mTmpRect2);
}
- if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
+ if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
}
} finally {
@@ -819,8 +822,8 @@
}
if (!deferEnsuringVisibility) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -854,10 +857,10 @@
/** Resume next focusable stack after reparenting to another display. */
void postReparent() {
adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
// Update visibility of activities before notifying WM. This way it won't try to resize
// windows that are no longer visible.
- mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+ mRootActivityContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
!PRESERVE_WINDOWS);
}
@@ -882,7 +885,7 @@
}
ActivityDisplay getDisplay() {
- return mStackSupervisor.getActivityDisplay(mDisplayId);
+ return mRootActivityContainer.getActivityDisplay(mDisplayId);
}
/**
@@ -1034,7 +1037,7 @@
}
/**
- * This is a simplified version of topRunningActivityLocked that provides a number of
+ * This is a simplified version of topRunningActivity that provides a number of
* optional skip-over modes. It is intended for use with the ActivityController hook only.
*
* @param token If non-null, any history records matching this token will be skipped.
@@ -1104,8 +1107,8 @@
if (r == null) {
return null;
}
- final TaskRecord task = r.getTask();
- final ActivityStack stack = r.getStack();
+ final TaskRecord task = r.getTaskRecord();
+ final ActivityStack stack = r.getActivityStack();
if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
if (stack != this) Slog.w(TAG,
"Illegal state! task does not point to stack it is in.");
@@ -1236,7 +1239,7 @@
boolean isFocusable() {
final ActivityRecord r = topRunningActivityLocked();
- return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
+ return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable());
}
boolean isFocusableAndVisible() {
@@ -1280,7 +1283,7 @@
// Overlays should not be considered as the task's logical top activity.
final ActivityRecord r = task.getTopActivity(false /* includeOverlays */);
- if (r == null || r.finishing || r.userId != userId ||
+ if (r == null || r.finishing || r.mUserId != userId ||
r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
continue;
@@ -1307,7 +1310,7 @@
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
+ taskIntent.getComponent().flattenToShortString()
- + "/aff=" + r.getTask().rootAffinity + " to new cls="
+ + "/aff=" + r.getTaskRecord().rootAffinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
// TODO Refactor to remove duplications. Check if logic can be simplified.
if (taskIntent != null && taskIntent.getComponent() != null &&
@@ -1366,7 +1369,7 @@
if (!r.okToShowLocked()) {
continue;
}
- if (!r.finishing && r.userId == userId) {
+ if (!r.finishing && r.mUserId == userId) {
if (compareIntentFilters) {
if (r.intent.filterEquals(intent)) {
return r;
@@ -1398,7 +1401,7 @@
final TaskRecord task = mTaskHistory.get(i);
if (task.okToShowLocked()) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUser: stack=" + getStackId() +
" moving " + task + " to top");
mTaskHistory.remove(i);
mTaskHistory.add(task);
@@ -1450,7 +1453,7 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord ar = activities.get(activityNdx);
- if ((userId == ar.userId) && packageName.equals(ar.packageName)) {
+ if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
ar.updateApplicationInfo(aInfo);
}
}
@@ -1536,7 +1539,7 @@
private boolean containsActivityFromStack(List<ActivityRecord> rs) {
for (ActivityRecord r : rs) {
- if (r.getStack() == this) {
+ if (r.getActivityStack() == this) {
return true;
}
}
@@ -1587,7 +1590,7 @@
if (prev == null) {
if (resuming == null) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return false;
}
@@ -1604,7 +1607,7 @@
mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
prev.setState(PAUSING, "startPausingLocked");
- prev.getTask().touchActiveTime();
+ prev.getTaskRecord().touchActiveTime();
clearLaunchTime(prev);
mService.updateCpuStats();
@@ -1612,7 +1615,7 @@
if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
- EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
+ EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
mService.updateUsageStats(prev, false);
@@ -1665,7 +1668,7 @@
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
if (resuming == null) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return false;
}
@@ -1690,7 +1693,7 @@
return;
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
- r.userId, System.identityHashCode(r), r.shortComponentName,
+ r.mUserId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
if (r.isState(PAUSING)) {
@@ -1704,7 +1707,7 @@
}
}
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
@@ -1757,9 +1760,9 @@
}
if (resumeNext) {
- final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
+ mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
} else {
checkReadyForSleep();
ActivityRecord top = topStack.topRunningActivityLocked();
@@ -1768,7 +1771,7 @@
// something. Also if the top activity on the stack is not the just paused
// activity, we need to go ahead and resume it to ensure we complete an
// in-flight app switch.
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -1799,7 +1802,7 @@
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
- mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
}
private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
@@ -2011,7 +2014,7 @@
/**
* Ensure visibility with an option to also update the configuration of visible activities.
* @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
- * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+ * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
*/
// TODO: Should be re-worked based on the fact that each task as a stack in most cases.
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
@@ -2032,7 +2035,7 @@
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
+ boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
&& (isInStackLocked(starting) == null);
final boolean isTopNotPinnedStack =
isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -2443,7 +2446,7 @@
*
* NOTE: It is not safe to call this method directly as it can cause an activity in a
* non-focused stack to be resumed.
- * Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
+ * Use {@link RootActivityContainer#resumeFocusedStacksTopActivities} to resume the
* right activity for the current system state.
*/
@GuardedBy("mService")
@@ -2513,7 +2516,7 @@
return false;
}
- mStackSupervisor.cancelInitializingActivities();
+ mRootActivityContainer.cancelInitializingActivities();
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
@@ -2536,7 +2539,6 @@
executeAppTransition(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Top activity resumed " + next);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -2544,7 +2546,7 @@
// activity is paused, well that is the state we want.
if (shouldSleepOrShutDownActivities()
&& mLastPausedActivity == next
- && mStackSupervisor.allPausedActivitiesComplete()) {
+ && mRootActivityContainer.allPausedActivitiesComplete()) {
// If the current top activity may be able to occlude keyguard but the occluded state
// has not been set, update visibility and check again if we should continue to resume.
boolean nothingToResume = true;
@@ -2565,7 +2567,6 @@
executeAppTransition(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Going to sleep and all paused");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
}
@@ -2573,10 +2574,9 @@
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
- if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
+ if (!mService.mAmInternal.hasStartedUserState(next.mUserId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.userId + " is stopped");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ + ": user " + next.mUserId + " is stopped");
return false;
}
@@ -2590,10 +2590,9 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
// If we are currently pausing an activity, then don't do anything until that is done.
- if (!mStackSupervisor.allPausedActivitiesComplete()) {
+ if (!mRootActivityContainer.allPausedActivitiesComplete()) {
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"resumeTopActivityLocked: Skip resume: some activity pausing.");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -2640,7 +2639,6 @@
next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
}
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (lastResumed != null) {
lastResumed.setWillCloseOrEnterPip(true);
}
@@ -2655,7 +2653,6 @@
executeAppTransition(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2673,7 +2670,7 @@
if (prev != null && prev != next) {
if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
- && next != null && !next.nowVisible) {
+ && !next.nowVisible) {
mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Resuming top, waiting visible to hide: " + prev);
@@ -2706,7 +2703,7 @@
// considered stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.userId); /* TODO: Verify if correct userid */
+ next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
} catch (RemoteException e1) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
@@ -2727,7 +2724,7 @@
dwc.prepareAppTransition(TRANSIT_NONE, false);
} else {
dwc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
+ prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_CLOSE
: TRANSIT_TASK_CLOSE, false);
}
prev.setVisibility(false);
@@ -2739,7 +2736,7 @@
dwc.prepareAppTransition(TRANSIT_NONE, false);
} else {
dwc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
+ prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_OPEN
: next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
: TRANSIT_TASK_OPEN, false);
}
@@ -2814,7 +2811,7 @@
// result of invisible window resize.
// TODO: Remove this once visibilities are set correctly immediately when
// starting an activity.
- notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+ notUpdated = !mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
true /* markFrozenIfConfigChanged */, false /* deferResume */);
}
@@ -2836,7 +2833,6 @@
next.setVisibility(true);
}
next.completeResumeLocked();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2863,8 +2859,8 @@
// Clear app token stopped state in window manager if needed.
next.notifyAppResumed(next.stopped);
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
- System.identityHashCode(next), next.getTask().taskId,
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId,
+ System.identityHashCode(next), next.getTaskRecord().taskId,
next.shortComponentName);
next.sleeping = false;
@@ -2899,7 +2895,6 @@
false /* taskSwitch */);
}
mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2913,7 +2908,6 @@
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
} else {
@@ -2931,7 +2925,6 @@
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2942,7 +2935,7 @@
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
- return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+ return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
null /* targetOptions */);
}
@@ -2950,8 +2943,7 @@
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityInNextFocusableStack: " + reason + ", go home");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
+ return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
}
/** Returns the position the input task should be placed in this stack. */
@@ -3017,7 +3009,7 @@
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
- TaskRecord rTask = r.getTask();
+ TaskRecord rTask = r.getTaskRecord();
final int taskId = rTask.taskId;
// mLaunchTaskBehind tasks get placed at the back of the task stack.
if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
@@ -3043,7 +3035,7 @@
if (!startIt) {
if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
+ task, new RuntimeException("here").fillInStackTrace());
- r.createWindowContainer();
+ r.createAppWindowToken();
ActivityOptions.abort(options);
return;
}
@@ -3058,7 +3050,7 @@
// If we are not placing the new activity frontmost, we do not want to deliver the
// onUserLeaving callback to the actual frontmost activity
- final TaskRecord activityTask = r.getTask();
+ final TaskRecord activityTask = r.getTaskRecord();
if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
mStackSupervisor.mUserLeaving = false;
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
@@ -3073,9 +3065,10 @@
// TODO: Need to investigate if it is okay for the controller to already be created by the
// time we get to this point. I think it is, but need to double check.
// Use test in b/34179495 to trace the call path.
- if (r.getWindowContainerController() == null) {
- r.createWindowContainer();
+ if (r.mAppWindowToken == null) {
+ r.createAppWindowToken();
}
+
task.setFrontOfTask();
if (!isHomeOrRecentsStack() || numActivities() > 0) {
@@ -3130,12 +3123,12 @@
// "has the same starting icon" as the next one. This allows the
// window manager to keep the previous window it had previously
// created, if it still had one.
- TaskRecord prevTask = r.getTask();
+ TaskRecord prevTask = r.getTaskRecord();
ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
if (prev != null) {
// We don't want to reuse the previous starting preview if:
// (1) The current activity is in a different task.
- if (prev.getTask() != prevTask) {
+ if (prev.getTaskRecord() != prevTask) {
prev = null;
}
// (2) The current activity is already displayed.
@@ -3168,7 +3161,7 @@
return false;
}
final ActivityStack targetStack = toFrontTask != null
- ? toFrontTask.getStack() : toFrontActivity.getStack();
+ ? toFrontTask.getStack() : toFrontActivity.getActivityStack();
if (targetStack != null && targetStack.isActivityTypeAssistant()) {
// Ensure the task/activity being brought forward is not the assistant
return false;
@@ -3178,7 +3171,7 @@
private boolean isTaskSwitch(ActivityRecord r,
ActivityRecord topFocusedActivity) {
- return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
+ return topFocusedActivity != null && r.getTaskRecord() != topFocusedActivity.getTaskRecord();
}
/**
@@ -3245,16 +3238,16 @@
!mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
mTaskHistory.get(0).mActivities.get(0) : null;
if (bottom != null && target.taskAffinity != null
- && target.taskAffinity.equals(bottom.getTask().affinity)) {
+ && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
// then merge it into the same task.
- targetTask = bottom.getTask();
+ targetTask = bottom.getTaskRecord();
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
+ " out to bottom task " + targetTask);
} else {
targetTask = createTaskRecord(
- mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
+ mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId),
target.info, null, null, null, false);
targetTask.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
@@ -3448,7 +3441,7 @@
ActivityRecord newActivity) {
final boolean forceReset =
(newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- final TaskRecord task = taskTop.getTask();
+ final TaskRecord task = taskTop.getTaskRecord();
/** False until we evaluate the TaskRecord associated with taskTop. Switches to true
* for remaining tasks. Used for later tasks to reparent to task. */
@@ -3497,7 +3490,7 @@
if (callingUid > 0) {
mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
- data, r.getUriPermissionsLocked(), r.userId);
+ data, r.getUriPermissionsLocked(), r.mUserId);
}
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -3536,7 +3529,7 @@
}
private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
- if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
((mResumedActivity != r) && (mResumedActivity != null))) {
return;
}
@@ -3545,7 +3538,7 @@
final String myReason = reason + " adjustFocus";
if (next == r) {
- final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+ final ActivityRecord top = mRootActivityContainer.topRunningActivity();
if (top != null) {
top.moveFocusableActivityToTop(myReason);
}
@@ -3559,7 +3552,7 @@
// Task is not guaranteed to be non-null. For example, destroying the
// {@link ActivityRecord} will disassociate the task from the activity.
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task == null) {
throw new IllegalStateException("activity no longer associated with task:" + r);
@@ -3569,7 +3562,7 @@
final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
if (nextFocusableStack != null) {
final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
- if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
+ if (top != null && top == mRootActivityContainer.getTopResumedActivity()) {
// TODO(b/111361570): Remove this and update focused app per-display in
// WindowManager every time an activity becomes resumed in
// ActivityTaskManagerService#setResumedActivityUncheckLocked().
@@ -3597,7 +3590,7 @@
*/
private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
final ActivityStack stack =
- mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
+ mRootActivityContainer.getNextFocusableStack(this, !allowFocusSelf);
final String myReason = reason + " adjustFocusToNextFocusableStack";
if (stack == null) {
return null;
@@ -3651,7 +3644,7 @@
r.setVisible(false);
}
EventLogTags.writeAmStopActivity(
- r.userId, System.identityHashCode(r), r.shortComponentName);
+ r.mUserId, System.identityHashCode(r), r.shortComponentName);
mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
StopActivityItem.obtain(r.visible, r.configChangeFlags));
if (shouldSleepOrShutDownActivities()) {
@@ -3727,7 +3720,7 @@
}
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishedTask = r.getTask();
+ finishedTask = r.getTaskRecord();
int taskNdx = mTaskHistory.indexOf(finishedTask);
final TaskRecord task = finishedTask;
int activityNdx = task.mActivities.indexOf(r);
@@ -3801,7 +3794,7 @@
}
final boolean finishActivityAffinityLocked(ActivityRecord r) {
- ArrayList<ActivityRecord> activities = r.getTask().mActivities;
+ ArrayList<ActivityRecord> activities = r.getTaskRecord().mActivities;
for (int index = activities.indexOf(r); index >= 0; --index) {
ActivityRecord cur = activities.get(index);
if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
@@ -3819,15 +3812,15 @@
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo
+ " who=" + r.resultWho + " req=" + r.requestCode
+ " res=" + resultCode + " data=" + resultData);
- if (resultTo.userId != r.userId) {
+ if (resultTo.mUserId != r.mUserId) {
if (resultData != null) {
- resultData.prepareToLeaveUser(r.userId);
+ resultData.prepareToLeaveUser(r.mUserId);
}
}
if (r.info.applicationInfo.uid > 0) {
mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
resultTo.packageName, resultData,
- resultTo.getUriPermissionsLocked(), resultTo.userId);
+ resultTo.getUriPermissionsLocked(), resultTo.mUserId);
}
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
r.resultTo = null;
@@ -3865,9 +3858,9 @@
mWindowManager.deferSurfaceLayout();
try {
r.makeFinishingLocked();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- r.userId, System.identityHashCode(r),
+ r.mUserId, System.identityHashCode(r),
task.taskId, r.shortComponentName, reason);
final ArrayList<ActivityRecord> activities = task.mActivities;
final int index = activities.indexOf(r);
@@ -4000,7 +3993,7 @@
r.setState(FINISHING, "finishCurrentActivityLocked");
final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
- && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+ && prevState == PAUSED && (r.getActivityStack() != display.getFocusedStack()
|| (next == null && display.topRunningActivity() == null));
if (mode == FINISH_IMMEDIATELY
@@ -4018,11 +4011,11 @@
// stack, need to make something visible in its place. Also if the display does not
// have running activity, the configuration may need to be updated for restoring
// original orientation of the display.
- mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+ mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -4035,7 +4028,7 @@
if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
return r;
}
@@ -4076,15 +4069,15 @@
boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
// Basic case: for simple app-centric recents, we need to recreate
// the task if the affinity has changed.
- if (srec == null || srec.getTask().affinity == null ||
- !srec.getTask().affinity.equals(destAffinity)) {
+ if (srec == null || srec.getTaskRecord().affinity == null ||
+ !srec.getTaskRecord().affinity.equals(destAffinity)) {
return true;
}
// Document-centric case: an app may be split in to multiple documents;
// they need to re-create their task if this current activity is the root
// of a document, unless simply finishing it will return them to the the
// correct app behind.
- final TaskRecord task = srec.getTask();
+ final TaskRecord task = srec.getTaskRecord();
if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) {
// Okay, this activity is at the root of its task. What to do, what to do...
if (!inFrontOfStandardStack()) {
@@ -4108,7 +4101,7 @@
final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
Intent resultData) {
- final TaskRecord task = srec.getTask();
+ final TaskRecord task = srec.getTaskRecord();
final ArrayList<ActivityRecord> activities = task.mActivities;
final int start = activities.indexOf(srec);
if (!mTaskHistory.contains(task) || (start < 0)) {
@@ -4173,7 +4166,7 @@
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
- srec.userId);
+ srec.mUserId);
// TODO(b/64750076): Check if calling pid should really be -1.
final int res = mService.getActivityStartController()
.obtainStarter(destIntent, "navigateUpTo")
@@ -4290,7 +4283,7 @@
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
r.app = null;
r.removeWindowContainer();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
final boolean lastActivity = task != null ? task.removeActivity(r) : false;
// If we are removing the last activity in the task, not including task overlay activities,
// then fall through into the block below to remove the entire task itself
@@ -4377,7 +4370,7 @@
}
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -4454,8 +4447,8 @@
}
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.getTask().taskId, r.shortComponentName, reason);
+ r.mUserId, System.identityHashCode(r),
+ r.getTaskRecord().taskId, r.shortComponentName, reason);
boolean removedFromHistory = false;
@@ -4568,7 +4561,7 @@
}
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
@@ -4654,8 +4647,8 @@
if (!r.finishing) {
Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.getTask().taskId, r.shortComponentName,
+ r.mUserId, System.identityHashCode(r),
+ r.getTaskRecord().taskId, r.shortComponentName,
"proc died without state saved");
if (r.getState() == RESUMED) {
mService.updateUsageStats(r, false);
@@ -4712,7 +4705,7 @@
task.mLastTimeMoved *= -1;
}
}
- mStackSupervisor.invalidateTaskLayers();
+ mRootActivityContainer.invalidateTaskLayers();
}
final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
@@ -4754,7 +4747,7 @@
final ActivityRecord top = tr.getTopActivity();
if (top == null || !top.okToShowLocked()) {
if (top != null) {
- mStackSupervisor.mRecentTasks.add(top.getTask());
+ mStackSupervisor.mRecentTasks.add(top.getTaskRecord());
}
ActivityOptions.abort(options);
return;
@@ -4788,7 +4781,7 @@
topActivity.supportsEnterPipOnTaskSwitch = true;
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
@@ -4860,7 +4853,7 @@
return true;
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
return true;
}
@@ -4869,7 +4862,7 @@
final String strData = data != null ? data.toSafeString() : null;
EventLog.writeEvent(tag,
- r.userId, System.identityHashCode(r), task.taskId,
+ r.mUserId, System.identityHashCode(r), task.taskId,
r.shortComponentName, r.intent.getAction(),
r.intent.getType(), strData, r.intent.getFlags());
}
@@ -4882,15 +4875,15 @@
return;
}
- final TaskRecord startTask = start.getTask();
+ final TaskRecord startTask = start.getTaskRecord();
boolean behindFullscreen = false;
boolean updatedConfig = false;
for (int taskIndex = mTaskHistory.indexOf(startTask); taskIndex >= 0; --taskIndex) {
final TaskRecord task = mTaskHistory.get(taskIndex);
final ArrayList<ActivityRecord> activities = task.mActivities;
- int activityIndex =
- (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
+ int activityIndex = (start.getTaskRecord() == task)
+ ? activities.indexOf(start) : activities.size() - 1;
for (; activityIndex >= 0; --activityIndex) {
final ActivityRecord r = activities.get(activityIndex);
updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
@@ -4907,7 +4900,7 @@
if (updatedConfig) {
// Ensure the resumed state of the focus activity if we updated the configuration of
// any activity.
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -4942,8 +4935,8 @@
// 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());
+ if (task.getRequestedOverrideBounds() != null) {
+ mTmpRect2.set(task.getRequestedOverrideBounds());
fitWithinBounds(mTmpRect2, bounds);
task.updateOverrideConfiguration(mTmpRect2);
}
@@ -4952,9 +4945,12 @@
}
}
- 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);
}
}
@@ -5054,10 +5050,10 @@
ActivityRecord r = mTmpActivities.remove(0);
final boolean sameComponent =
(r.packageName.equals(packageName) && (filterByClasses == null
- || filterByClasses.contains(r.realActivity.getClassName())))
- || (packageName == null && r.userId == userId);
- if ((userId == UserHandle.USER_ALL || r.userId == userId)
- && (sameComponent || r.getTask() == lastTask)
+ || filterByClasses.contains(r.mActivityComponent.getClassName())))
+ || (packageName == null && r.mUserId == userId);
+ if ((userId == UserHandle.USER_ALL || r.mUserId == userId)
+ && (sameComponent || r.getTaskRecord() == lastTask)
&& (r.app == null || evenPersistent || !r.app.isPersistent())) {
if (!doit) {
if (r.finishing) {
@@ -5068,11 +5064,11 @@
return true;
}
if (r.isActivityTypeHome()) {
- if (homeActivity != null && homeActivity.equals(r.realActivity)) {
+ if (homeActivity != null && homeActivity.equals(r.mActivityComponent)) {
Slog.i(TAG, "Skip force-stop again " + r);
continue;
} else {
- homeActivity = r.realActivity;
+ homeActivity = r.mActivityComponent;
}
}
didSomething = true;
@@ -5083,7 +5079,7 @@
}
r.app = null;
}
- lastTask = r.getTask();
+ lastTask = r.getTaskRecord();
finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
true);
}
@@ -5099,7 +5095,7 @@
*/
void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
- boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
+ boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
boolean topTask = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -5164,7 +5160,7 @@
return removeHistoryRecordsForAppLocked(app);
}
- void handleAppCrashLocked(WindowProcessController app) {
+ void handleAppCrash(WindowProcessController app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5196,7 +5192,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);
@@ -5311,7 +5307,7 @@
// We only need to adjust focused stack if this stack is in focus and we are not in the
// process of moving the task to the top of the stack that will be focused.
if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
- && mStackSupervisor.isTopDisplayFocusedStack(this)) {
+ && mRootActivityContainer.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
getDisplay().moveHomeStackToFront(myReason);
@@ -5354,7 +5350,7 @@
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);
return task;
@@ -5417,7 +5413,7 @@
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
@@ -5484,7 +5480,7 @@
moveToFront(reason);
// If the original state is resumed, there is no state change to update focused app.
// So here makes sure the activity focus is set if it is the top.
- if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+ if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) {
// TODO(b/111361570): Support multiple focused apps in WM
mService.setResumedActivityUncheckLocked(r, reason);
}
@@ -5546,7 +5542,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 694e9d1..d92a9f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -25,28 +25,18 @@
import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WaitResult.INVALID_DELAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
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.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -59,26 +49,13 @@
import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
-import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_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;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -86,9 +63,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -96,6 +71,10 @@
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -103,27 +82,15 @@
import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-import static java.lang.Integer.MAX_VALUE;
-
import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
-import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.LaunchActivityItem;
@@ -139,17 +106,10 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
-import android.os.FactoryTest;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -163,20 +123,13 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.MediaStore;
-import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.EventLog;
-import android.util.IntArray;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.DisplayInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -185,34 +138,28 @@
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
-import com.android.server.am.AppTimeTracker;
import com.android.server.am.EventLogTags;
import com.android.server.am.UserState;
-import com.android.server.wm.ActivityStack.ActivityState;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import java.util.Set;
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
- RecentTasks.Callbacks, RootWindowContainerListener {
+// TODO: This class has become a dumping ground. Let's
+// - Move things relating to the hierarchy to RootWindowContainer
+// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
+// - Move interface things to ActivityTaskManagerService.
+// - All other little things to other files.
+public class ActivityStackSupervisor implements RecentTasks.Callbacks {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
- private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
- static final String TAG_STATES = TAG + POSTFIX_STATES;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
/** How long we wait until giving up on the last activity telling us it is idle. */
@@ -233,12 +180,6 @@
static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
- private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
-
- // Used to indicate if an object (e.g. stack) that we are trying to get
- // should be created if it doesn't exist already.
- static final boolean CREATE_IF_NEEDED = true;
-
// Used to indicate that windows of activities should be preserved during the resize.
static final boolean PRESERVE_WINDOWS = true;
@@ -270,25 +211,6 @@
private Rect mPendingTempOtherTaskBounds;
private Rect mPendingTempOtherTaskInsetBounds;
- /**
- * The modes which affect which tasks are returned when calling
- * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- MATCH_TASK_IN_STACKS_ONLY,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
- })
- public @interface AnyTaskForIdMatchTaskMode {}
- // Match only tasks in the current stacks
- static final int MATCH_TASK_IN_STACKS_ONLY = 0;
- // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
- static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
- // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
- // provided stack id
- static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
-
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -315,20 +237,20 @@
/** The number of distinct task ids that can be assigned to the tasks of a single user */
private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
- ActivityTaskManagerService mService;
+ final ActivityTaskManagerService mService;
+ RootActivityContainer mRootActivityContainer;
/** The historial list of recent tasks including inactive tasks */
RecentTasks mRecentTasks;
/** Helper class to abstract out logic for fetching the set of currently running tasks */
- private RunningTasks mRunningTasks;
+ RunningTasks mRunningTasks;
final ActivityStackSupervisorHandler mHandler;
final Looper mLooper;
/** Short cut */
WindowManagerService mWindowManager;
- DisplayManager mDisplayManager;
/** Common synchronization logic used to save things to disks. */
PersisterQueue mPersisterQueue;
@@ -341,9 +263,6 @@
*/
private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
- /** The current user */
- int mCurrentUser;
-
/** List of activities that are waiting for a new activity to become visible before completing
* whatever operation they are supposed to do. */
// TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
@@ -392,9 +311,6 @@
* is being brought in front of us. */
boolean mUserLeaving = false;
- /** Set when a power hint has started, but not ended. */
- private boolean mPowerHintSent;
-
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -410,29 +326,6 @@
*/
PowerManager.WakeLock mGoingToSleep;
- /**
- * A list of tokens that cause the top activity to be put to sleep.
- * They are used by components that may hide and block interaction with underlying
- * activities.
- */
- final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
-
- /** Stack id of the front stack when user switched, indexed by userId. */
- SparseIntArray mUserStackInFront = new SparseIntArray(2);
-
- /** Reference to default display so we can quickly look it up. */
- private ActivityDisplay mDefaultDisplay;
-
- /**
- * List of displays which contain activities, sorted by z-order.
- * The last entry in the list is the topmost.
- */
- private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
-
- private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
-
- private DisplayManagerInternal mDisplayManagerInternal;
-
/** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
boolean inResumeTopActivity;
@@ -443,50 +336,8 @@
private final Rect tempRect = new Rect();
private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
- // The default minimal size that will be used if the activity doesn't specify its minimal size.
- // It will be calculated when the default display gets added.
- int mDefaultMinSizeOfResizeableTaskDp = -1;
-
- // Whether tasks have moved and we need to rank the tasks before next OOM scoring
- private boolean mTaskLayersChanged = true;
-
private ActivityMetricsLogger mActivityMetricsLogger;
- private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
-
- @Override
- protected int getChildCount() {
- return mActivityDisplays.size();
- }
-
- @Override
- protected ActivityDisplay getChildAt(int index) {
- return mActivityDisplays.get(index);
- }
-
- @Override
- protected ConfigurationContainer getParent() {
- return null;
- }
-
- Configuration getDisplayOverrideConfiguration(int displayId) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
-
- return activityDisplay.getOverrideConfiguration();
- }
-
- void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
-
- activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
- }
-
/** Check if placing task or activity on specified display is allowed. */
boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
ActivityInfo activityInfo) {
@@ -508,44 +359,6 @@
}
/**
- * Check if configuration of specified display matches current global config.
- * Used to check if we can put a non-resizeable activity on a secondary display and it will get
- * the same config as on the default display.
- * @param displayId Id of the display to check.
- * @return {@code true} if configuration matches.
- */
- private boolean displayConfigMatchesGlobal(int displayId) {
- if (displayId == DEFAULT_DISPLAY) {
- return true;
- }
- if (displayId == INVALID_DISPLAY) {
- return false;
- }
- final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (targetDisplay == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
- return getConfiguration().equals(targetDisplay.getConfiguration());
- }
-
- static class FindTaskResult {
- ActivityRecord mRecord;
- boolean mIdealMatch;
-
- void clear() {
- mRecord = null;
- mIdealMatch = false;
- }
-
- void setTo(FindTaskResult result) {
- mRecord = result.mRecord;
- mIdealMatch = result.mIdealMatch;
- }
- }
-
- private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-
- /**
* Used to keep track whether app visibilities got changed since the last pause. Useful to
* determine whether to invoke the task stack change listener after pausing.
*/
@@ -565,11 +378,6 @@
*/
private boolean mAllowDockedStackResize = true;
- /**
- * Is dock currently minimized.
- */
- boolean mIsDockMinimized;
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -577,8 +385,6 @@
private boolean mInitialized;
- private RootWindowContainerController mWindowContainerController;
-
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -617,16 +423,6 @@
mHandler = new ActivityStackSupervisorHandler(looper);
}
- @VisibleForTesting
- void setService(ActivityTaskManagerService service) {
- mService = service;
- }
-
- @VisibleForTesting
- void setWindowContainerController(RootWindowContainerController controller) {
- mWindowContainerController = controller;
- }
-
public void initialize() {
if (mInitialized) {
return;
@@ -634,7 +430,9 @@
mInitialized = true;
mRunningTasks = createRunningTasks();
- mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
+
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
+ mHandler.getLooper());
mKeyguardController = new KeyguardController(mService, this);
mPersisterQueue = new PersisterQueue();
@@ -681,321 +479,16 @@
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
getKeyguardController().setWindowManager(wm);
- setWindowContainerController(new RootWindowContainerController(this));
-
- mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
- mDisplayManager.registerDisplayListener(this, mHandler);
- mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-
- final Display[] displays = mDisplayManager.getDisplays();
- for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
- final Display display = displays[displayNdx];
- final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
- if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
- mDefaultDisplay = activityDisplay;
- }
- addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
- }
- calculateDefaultMinimalSizeOfResizeableTasks();
-
- final ActivityDisplay defaultDisplay = getDefaultDisplay();
-
- defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
- }
-
- /** Change the z-order of the given display. */
- private void positionChildAt(ActivityDisplay display, int position) {
- if (position >= mActivityDisplays.size()) {
- position = mActivityDisplays.size() - 1;
- } else if (position < 0) {
- position = 0;
- }
-
- if (mActivityDisplays.isEmpty()) {
- mActivityDisplays.add(display);
- } else if (mActivityDisplays.get(position) != display) {
- mActivityDisplays.remove(display);
- mActivityDisplays.add(position, display);
- }
- }
-
- @Override
- public void onChildPositionChanged(DisplayWindowController childController, 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);
- }
- }
-
- ActivityStack getTopDisplayFocusedStack() {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
- if (focusedStack != null) {
- return focusedStack;
- }
- }
- return null;
- }
-
- ActivityRecord getTopResumedActivity() {
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- if (focusedStack == null) {
- return null;
- }
- final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
- if (resumedActivity != null && resumedActivity.app != null) {
- return resumedActivity;
- }
- // The top focused stack might not have a resumed activity yet - look on all displays in
- // focus order.
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
- if (resumedActivityOnDisplay != null) {
- return resumedActivityOnDisplay;
- }
- }
- return null;
- }
-
- boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
- if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
- return false;
- }
-
- return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
- }
-
- boolean isTopDisplayFocusedStack(ActivityStack stack) {
- return stack != null && stack == getTopDisplayFocusedStack();
}
void moveRecentsStackToFront(String reason) {
- final ActivityStack recentsStack = getDefaultDisplay().getStack(
+ final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
recentsStack.moveToFront(reason);
}
}
- boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
- if (!mService.isBooting() && !mService.isBooted()) {
- // Not ready yet!
- return false;
- }
-
- if (displayId == INVALID_DISPLAY) {
- displayId = DEFAULT_DISPLAY;
- }
-
- final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
- final String myReason = reason + " resumeHomeActivity";
-
- // Only resume home activity if isn't finishing.
- if (r != null && !r.finishing) {
- r.moveFocusableActivityToTop(myReason);
- return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
- }
- return startHomeOnDisplay(mCurrentUser, myReason, displayId);
- }
-
- /**
- * Check if home activity start should be allowed on a display.
- * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
- * @param displayId The id of the target display.
- * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
- * @return {@code true} if allow to launch, {@code false} otherwise.
- */
- boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
- boolean allowInstrumenting) {
- if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
- && mService.mTopAction == null) {
- // We are running in factory test mode, but unable to find the factory test app, so
- // just sit around displaying the error message and don't try to start anything.
- return false;
- }
-
- final WindowProcessController app =
- mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
- if (!allowInstrumenting && app != null && app.isInstrumenting()) {
- // Don't do this if the home app is currently being instrumented.
- return false;
- }
-
- if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
- && displayId == mService.mVr2dDisplayId)) {
- // No restrictions to default display or vr 2d display.
- return true;
- }
-
- final ActivityDisplay display = getActivityDisplay(displayId);
- if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
- // Can't launch home on display that doesn't support system decorations.
- return false;
- }
-
- final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
- && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
- && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
- if (!supportMultipleInstance) {
- // Can't launch home on other displays if it requested to be single instance. Also we
- // don't allow home applications that target before Q to have multiple home activity
- // instances because they may not be expected to have multiple home scenario and
- // haven't explicitly request for single instance.
- return false;
- }
-
- return true;
- }
-
- TaskRecord anyTaskForIdLocked(int id) {
- return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
- }
-
- TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
- return anyTaskForIdLocked(id, matchMode, null, !ON_TOP);
- }
-
- /**
- * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
- * @param id Id of the task we would like returned.
- * @param matchMode The mode to match the given task id in.
- * @param aOptions The activity options to use for restoration. Can be null.
- * @param onTop If the stack for the task should be the topmost on the display.
- */
- TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
- @Nullable ActivityOptions aOptions, boolean onTop) {
- // If options are set, ensure that we are attempting to actually restore a task
- if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
- throw new IllegalArgumentException("Should not specify activity options for non-restore"
- + " lookup");
- }
-
- int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final TaskRecord task = stack.taskForIdLocked(id);
- if (task == null) {
- continue;
- }
- if (aOptions != null) {
- // Resolve the stack the task should be placed in now based on options
- // and reparent if needed.
- final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop);
- if (launchStack != null && stack != launchStack) {
- final int reparentMode = onTop
- ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
- task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
- "anyTaskForIdLocked");
- }
- }
- return task;
- }
- }
-
- // If we are matching stack tasks only, return now
- if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
- return null;
- }
-
- // Otherwise, check the recent tasks and return if we find it there and we are not restoring
- // the task from recents
- if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
- final TaskRecord task = mRecentTasks.getTask(id);
-
- if (task == null) {
- if (DEBUG_RECENTS) {
- Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
- }
-
- return null;
- }
-
- if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
- return task;
- }
-
- // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
- if (!restoreRecentTaskLocked(task, aOptions, onTop)) {
- if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
- "Couldn't restore task id=" + id + " found in recents");
- return null;
- }
- if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
- return task;
- }
-
- ActivityRecord isInAnyStackLocked(IBinder token) {
- int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.isInStackLocked(token);
- if (r != null) {
- return r;
- }
- }
- }
- return null;
- }
-
- /**
- * Detects whether we should show a lock screen in front of this task for a locked user.
- * <p>
- * We'll do this if either of the following holds:
- * <ul>
- * <li>The top activity explicitly belongs to {@param userId}.</li>
- * <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
- * </ul>
- *
- * @return {@code true} if the top activity looks like it belongs to {@param userId}.
- */
- private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
- // To handle the case that work app is in the task but just is not the top one.
- final ActivityRecord activityRecord = task.getTopActivity();
- final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
-
- return (activityRecord != null && activityRecord.userId == userId)
- || (resultTo != null && resultTo.userId == userId);
- }
-
- /**
- * Find all visible task stacks containing {@param userId} and intercept them with an activity
- * to block out the contents and possibly start a credential-confirming intent.
- *
- * @param userId user handle for the locked managed profile.
- */
- void lockAllProfileTasks(@UserIdInt int userId) {
- mWindowManager.deferSurfaceLayout();
- try {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final List<TaskRecord> tasks = stack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
- final TaskRecord task = tasks.get(taskNdx);
-
- // Check the task for a top activity belonging to userId, or returning a
- // result to an activity belonging to userId. Example case: a document
- // picker for personal files, opened by a work app, should still get locked.
- if (taskTopActivityIsUser(task, userId)) {
- mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
- task.taskId, userId);
- }
- }
- }
- }
- } finally {
- mWindowManager.continueSurfaceLayout();
- }
- }
-
void setNextTaskIdForUserLocked(int taskId, int userId) {
final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
if (taskId > currentTaskId) {
@@ -1019,7 +512,7 @@
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
while (mRecentTasks.containsTaskId(candidateTaskId, userId)
- || anyTaskForIdLocked(
+ || mRootActivityContainer.anyTaskForId(
candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
if (candidateTaskId == currentTaskId) {
@@ -1034,145 +527,6 @@
return candidateTaskId;
}
- boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
- final String processName = app.mName;
- boolean didSomething = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isTopDisplayFocusedStack(stack)) {
- continue;
- }
- stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
- final ActivityRecord top = stack.topRunningActivityLocked();
- final int size = mTmpActivityList.size();
- for (int i = 0; i < size; i++) {
- final ActivityRecord activity = mTmpActivityList.get(i);
- if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
- && processName.equals(activity.processName)) {
- try {
- if (realStartActivityLocked(activity, app,
- top == activity /* andResume */, true /* checkConfig */)) {
- didSomething = true;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception in new application when starting activity "
- + top.intent.getComponent().flattenToShortString(), e);
- throw e;
- }
- }
- }
- }
- }
- if (!didSomething) {
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- }
- return didSomething;
- }
-
- boolean allResumedActivitiesIdle() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- // TODO(b/117135575): Check resumed activities on all visible stacks.
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- if (display.isSleeping()) {
- // No resumed activities while display is sleeping.
- continue;
- }
-
- // If the focused stack is not null or not empty, there should have some activities
- // resuming or resumed. Make sure these activities are idle.
- final ActivityStack stack = display.getFocusedStack();
- if (stack == null || stack.numActivities() == 0) {
- continue;
- }
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity == null || !resumedActivity.idle) {
- if (DEBUG_STATES) {
- Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
- + stack.mStackId + " " + resumedActivity + " not idle");
- }
- return false;
- }
- }
- // Send launch end powerhint when idle
- sendPowerHintForLaunchEndIfNeeded();
- return true;
- }
-
- private boolean allResumedActivitiesVisible() {
- boolean foundResumed = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.getResumedActivity();
- if (r != null) {
- if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
- return false;
- }
- foundResumed = true;
- }
- }
- }
- return foundResumed;
- }
-
- private void executeAppTransitionForAllDisplay() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- display.getWindowContainerController().executeAppTransition();
- }
- }
-
- /**
- * Pause all activities in either all of the stacks or just the back stacks.
- * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
- * @param resuming The resuming activity.
- * @param dontWait The resuming activity isn't going to wait for all activities to be paused
- * before resuming.
- * @return true if any activity was paused as a result of this call.
- */
- boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
- boolean someActivityPaused = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- someActivityPaused |= mActivityDisplays.get(displayNdx)
- .pauseBackStacks(userLeaving, resuming, dontWait);
- }
- return someActivityPaused;
- }
-
- boolean allPausedActivitiesComplete() {
- boolean pausing = true;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.mPausingActivity;
- if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
- if (DEBUG_STATES) {
- Slog.d(TAG_STATES,
- "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
- pausing = false;
- } else {
- return false;
- }
- }
- }
- }
- return pausing;
- }
-
- void cancelInitializingActivities() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.cancelInitializingActivities();
- }
- }
- }
-
void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
mWaitingForActivityVisible.add(waitInfo);
@@ -1186,7 +540,7 @@
mActivitiesWaitingForVisibleActivity.remove(r);
for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
- if (mWaitingForActivityVisible.get(i).matches(r.realActivity)) {
+ if (mWaitingForActivityVisible.get(i).matches(r.mActivityComponent)) {
mWaitingForActivityVisible.remove(i);
}
}
@@ -1200,7 +554,7 @@
boolean changed = false;
for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
final WaitInfo w = mWaitingForActivityVisible.get(i);
- if (w.matches(r.realActivity)) {
+ if (w.matches(r.mActivityComponent)) {
final WaitResult result = w.getResult();
changed = true;
result.timeout = false;
@@ -1234,7 +588,7 @@
// Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
// will be no followup launch signals. Assign the result and launched component.
if (result == START_DELIVERED_TO_TOP) {
- w.who = r.realActivity;
+ w.who = r.mActivityComponent;
}
}
}
@@ -1263,24 +617,6 @@
}
}
- ActivityRecord topRunningActivityLocked() {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
- if (topActivity != null) {
- return topActivity;
- }
- }
- return null;
- }
-
- @VisibleForTesting
- void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
- @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
- int callingUid, boolean allowed) {
- mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
- mActivityDisplays, callingUid, allowed);
- }
-
ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
ProfilerInfo profilerInfo) {
final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
@@ -1326,31 +662,29 @@
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
- synchronized (mService.mGlobalLock) {
- try {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
- int modifiedFlags = flags
- | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
- if (intent.isWebIntent()
- || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
- modifiedFlags |= PackageManager.MATCH_INSTANT;
- }
-
- // In order to allow cross-profile lookup, we clear the calling identity here.
- // Note the binder identity won't affect the result, but filterCallingUid will.
-
- // Cross-user/profile call check are done at the entry points
- // (e.g. AMS.startActivityAsUser).
- final long token = Binder.clearCallingIdentity();
- try {
- return mService.getPackageManagerInternalLocked().resolveIntent(
- intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ try {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+ int modifiedFlags = flags
+ | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
+ if (intent.isWebIntent()
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+ modifiedFlags |= PackageManager.MATCH_INSTANT;
}
+
+ // In order to allow cross-profile lookup, we clear the calling identity here.
+ // Note the binder identity won't affect the result, but filterCallingUid will.
+
+ // Cross-user/profile call check are done at the entry points
+ // (e.g. AMS.startActivityAsUser).
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mService.getPackageManagerInternalLocked().resolveIntent(
+ intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -1360,10 +694,10 @@
return resolveActivity(intent, rInfo, startFlags, profilerInfo);
}
- private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
+ boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
- if (!allPausedActivitiesComplete()) {
+ if (!mRootActivityContainer.allPausedActivitiesComplete()) {
// While there are activities pausing we skipping starting any new activities until
// pauses are complete. NOTE: that we also do this for activities that are starting in
// the paused state because they will first be resumed then paused on the client side.
@@ -1373,7 +707,7 @@
return false;
}
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
final ActivityStack stack = task.getStack();
beginDeferResume();
@@ -1398,11 +732,11 @@
// Deferring resume here because we're going to launch new activity shortly.
// We don't want to perform a redundant launch of the same record while ensuring
// configurations and trying to resume top activity of focused stack.
- ensureVisibilityAndConfig(r, r.getDisplayId(),
+ mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
- if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
+ if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
true /* isTop */)) {
// We only set the visibility to true if the activity is allowed to be visible
// based on
@@ -1414,7 +748,7 @@
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
- if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
+ if ((r.mUserId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
Slog.wtf(TAG,
"User ID for activity changing for " + r
+ " appInfo.uid=" + r.appInfo.uid
@@ -1455,7 +789,7 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+ " newIntents=" + newIntents + " andResume=" + andResume);
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId,
System.identityHashCode(r), task.taskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
@@ -1568,7 +902,7 @@
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (isTopDisplayFocusedStack(stack)) {
+ if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
mService.getActivityStartController().startSetupActivity();
}
@@ -1581,47 +915,6 @@
return true;
}
- /**
- * Ensure all activities visibility, update orientation and configuration.
- *
- * @param starting The currently starting activity or {@code null} if there is none.
- * @param displayId The id of the display where operation is executed.
- * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
- * {@code true} if config changed.
- * @param deferResume Whether to defer resume while updating config.
- * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
- * because of configuration update.
- */
- boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
- boolean markFrozenIfConfigChanged, boolean deferResume) {
- // First ensure visibility without updating the config just yet. We need this to know what
- // activities are affecting configuration now.
- // Passing null here for 'starting' param value, so that visibility of actual starting
- // activity will be properly updated.
- ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, false /* notifyClients */);
-
- if (displayId == INVALID_DISPLAY) {
- // The caller didn't provide a valid display id, skip updating config.
- return true;
- }
-
- // Force-update the orientation from the WindowManager, since we need the true configuration
- // to send to the client now.
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- getDisplayOverrideConfiguration(displayId),
- starting != null && starting.mayFreezeScreenLocked(starting.app)
- ? starting.appToken : null,
- displayId, true /* forceUpdate */);
- if (starting != null && markFrozenIfConfigChanged && config != null) {
- starting.frozenBeforeDestroy = true;
- }
-
- // Update the configuration of the activities on the display.
- return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
- displayId);
- }
-
private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
int extrasSize = 0;
if (intent != null) {
@@ -1677,47 +970,6 @@
mService.mH.sendMessage(msg);
}
- void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
- boolean sendHint = forceSend;
-
- if (!sendHint) {
- // Send power hint if we don't know what we're launching yet
- sendHint = targetActivity == null || targetActivity.app == null;
- }
-
- if (!sendHint) { // targetActivity != null
- // Send power hint when the activity's process is different than the current resumed
- // activity on all displays, or if there are no resumed activities in the system.
- boolean noResumedActivities = true;
- boolean allFocusedProcessesDiffer = true;
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
- final WindowProcessController resumedActivityProcess =
- resumedActivity == null ? null : resumedActivity.app;
-
- noResumedActivities &= resumedActivityProcess == null;
- if (resumedActivityProcess != null) {
- allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
- }
- }
- sendHint = noResumedActivities || allFocusedProcessesDiffer;
- }
-
- if (sendHint && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
- mPowerHintSent = true;
- }
- }
-
- void sendPowerHintForLaunchEndIfNeeded() {
- // Trigger launch power hint if activity is launched
- if (mPowerHintSent && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
- mPowerHintSent = false;
- }
- }
-
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
int requestCode, int callingPid, int callingUid, String callingPackage,
boolean ignoreTargetSecurity, boolean launchingInTask,
@@ -1796,7 +1048,8 @@
return true;
}
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
+ final ActivityDisplay activityDisplay =
+ mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId);
if (activityDisplay == null || activityDisplay.isRemoved()) {
Slog.w(TAG, "Launch on display check: display not found");
return false;
@@ -1858,21 +1111,6 @@
return false;
}
- /** Update lists of UIDs that are present on displays and have access to them. */
- void updateUIDsPresentOnDisplay() {
- mDisplayAccessUIDs.clear();
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- // Only bother calculating the whitelist for private displays
- if (activityDisplay.isPrivate()) {
- mDisplayAccessUIDs.append(
- activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
- }
- }
- // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
- mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
- }
-
UserInfo getUserInfo(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -2024,7 +1262,8 @@
// Check if able to finish booting when device is booting and all resumed activities
// are idle.
- if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) {
+ if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle())
+ || fromTimeout) {
booting = checkFinishBootingLocked();
}
@@ -2033,7 +1272,7 @@
r.mRelaunchReason = RELAUNCH_REASON_NONE;
}
- if (allResumedActivitiesIdle()) {
+ if (mRootActivityContainer.allResumedActivitiesIdle()) {
if (r != null) {
mService.scheduleAppGcsLocked();
}
@@ -2046,7 +1285,7 @@
}
mLaunchingActivity.release();
}
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
// Atomically retrieve all of the other things to do.
@@ -2067,7 +1306,7 @@
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
if (stack != null) {
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
@@ -2082,7 +1321,7 @@
// waiting for the next one to start.
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
if (stack != null) {
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
}
@@ -2102,186 +1341,13 @@
//mWindowManager.dump();
if (activityRemoved) {
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return r;
}
- boolean handleAppDiedLocked(WindowProcessController app) {
- boolean hasVisibleActivities = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- hasVisibleActivities |= stack.handleAppDiedLocked(app);
- }
- }
- return hasVisibleActivities;
- }
-
- void closeSystemDialogsLocked() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.closeSystemDialogsLocked();
- }
- }
- }
-
- void removeUserLocked(int userId) {
- mUserStackInFront.delete(userId);
- }
-
- /**
- * Update the last used stack id for non-current user (current user's last
- * used stack is the focused stack)
- */
- void updateUserStackLocked(int userId, ActivityStack stack) {
- if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getStackId()
- : getDefaultDisplay().getHomeStack().mStackId);
- }
- }
-
- /**
- * @return true if some activity was finished (or would have finished if doit were true).
- */
- boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
- boolean didSomething = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (stack.finishDisabledPackageActivitiesLocked(
- packageName, filterByClasses, doit, evenPersistent, userId)) {
- didSomething = true;
- }
- }
- }
- return didSomething;
- }
-
- void updatePreviousProcessLocked(ActivityRecord r) {
- // Now that this process has stopped, we may want to consider
- // it to be the previous app to try to keep around in case
- // the user wants to return to it.
-
- // First, found out what is currently the foreground app, so that
- // we don't blow away the previous app if this activity is being
- // hosted by the process that is actually still the foreground.
- WindowProcessController fgApp = null;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (isTopDisplayFocusedStack(stack)) {
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null) {
- fgApp = resumedActivity.app;
- } else if (stack.mPausingActivity != null) {
- fgApp = stack.mPausingActivity.app;
- }
- break;
- }
- }
- }
-
- // Now set this one as the previous process, only if that really
- // makes sense to.
- if (r.hasProcess() && fgApp != null && r.app != fgApp
- && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
- && r.app != mService.mHomeProcess) {
- mService.mPreviousProcess = r.app;
- mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
- }
- }
-
- boolean resumeFocusedStacksTopActivitiesLocked() {
- return resumeFocusedStacksTopActivitiesLocked(null, null, null);
- }
-
- boolean resumeFocusedStacksTopActivitiesLocked(
- ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
-
- if (!readyToResume()) {
- return false;
- }
-
- if (targetStack != null && (targetStack.isTopStackOnDisplay()
- || getTopDisplayFocusedStack() == targetStack)) {
- return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
- }
-
- // Resume all top activities in focused stacks on all displays.
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final ActivityStack focusedStack = display.getFocusedStack();
- if (focusedStack == null) {
- continue;
- }
- final ActivityRecord r = focusedStack.topRunningActivityLocked();
- if (r == null || !r.isState(RESUMED)) {
- focusedStack.resumeTopActivityUncheckedLocked(null, null);
- } else if (r.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront operation.
- focusedStack.executeAppTransition(targetOptions);
- }
- }
-
- return false;
- }
-
- void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.updateActivityApplicationInfoLocked(aInfo);
- }
- }
- }
-
- /**
- * Finish the topmost activities in all stacks that belong to the crashed app.
- * @param app The app that crashed.
- * @param reason Reason to perform this action.
- * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
- */
- int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
- TaskRecord finishedTask = null;
- ActivityStack focusedStack = getTopDisplayFocusedStack();
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- // It is possible that request to finish activity might also remove its task and stack,
- // so we need to be careful with indexes in the loop and check child count every time.
- for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
- if (stack == focusedStack || finishedTask == null) {
- finishedTask = t;
- }
- }
- }
- return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
- }
-
- void finishVoiceTask(IVoiceInteractionSession session) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final int numStacks = display.getChildCount();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.finishVoiceTask(session);
- }
- }
- }
-
- /**
- * This doesn't just find a task, it also moves the task to front.
- */
+ /** This doesn't just find a task, it also moves the task to front. */
void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
ActivityStack currentStack = task.getStack();
@@ -2301,7 +1367,8 @@
final Rect bounds = options.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
- ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
+ ActivityStack stack =
+ mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP);
if (stack != currentStack) {
moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
@@ -2313,7 +1380,7 @@
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (stack.resizeStackWithLaunchBounds()) {
- resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
+ mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
@@ -2366,387 +1433,22 @@
return mLaunchParamsController;
}
- protected <T extends ActivityStack> T getStack(int stackId) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.get(i).getStack(stackId);
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ private void deferUpdateRecentsHomeStackBounds() {
+ mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
}
- /** @see ActivityDisplay#getStack(int, int) */
- private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
- if (stack != null) {
- return stack;
- }
- }
- return null;
- }
-
- int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord task) {
- // Preference is given to the activity type for the activity then the task since the type
- // once set shouldn't change.
- int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
- if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
- activityType = task.getActivityType();
- }
- if (activityType != ACTIVITY_TYPE_UNDEFINED) {
- return activityType;
- }
- if (options != null) {
- activityType = options.getLaunchActivityType();
- }
- return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
- }
-
- <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
- return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
- }
-
- /**
- * Returns the right stack to use for launching factoring in all the input parameters.
- *
- * @param r The activity we are trying to launch. Can be null.
- * @param options The activity options used to the launch. Can be null.
- * @param candidateTask The possible task the activity might be launched in. Can be null.
- * @params launchParams The resolved launch params to use.
- *
- * @return The stack to use for the launch or INVALID_STACK_ID.
- */
- <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- int taskId = INVALID_TASK_ID;
- int displayId = INVALID_DISPLAY;
- //Rect bounds = null;
-
- // We give preference to the launch preference in activity options.
- if (options != null) {
- taskId = options.getLaunchTaskId();
- }
-
- // First preference for stack goes to the task Id set in the activity options. Use the stack
- // associated with that if possible.
- if (taskId != INVALID_TASK_ID) {
- // Temporarily set the task id to invalid in case in re-entry.
- options.setLaunchTaskId(INVALID_TASK_ID);
- final TaskRecord task = anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
- options.setLaunchTaskId(taskId);
- if (task != null) {
- return task.getStack();
- }
- }
-
- final int activityType = resolveActivityType(r, options, candidateTask);
- T stack;
-
- // Next preference for stack goes to the display Id set the candidate display.
- if (launchParams != null) {
- displayId = launchParams.mPreferredDisplayId;
- }
- if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
- if (r != null) {
- stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
- launchParams);
- if (stack != null) {
- return stack;
- }
- }
- final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
- if (display != null) {
- stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
- if (stack != null) {
- return stack;
- }
- }
- }
-
- // Give preference to the stack and display of the input task and activity if they match the
- // mode we want to launch into.
- stack = null;
- ActivityDisplay display = null;
- if (candidateTask != null) {
- stack = candidateTask.getStack();
- }
- if (stack == null && r != null) {
- stack = r.getStack();
- }
- if (stack != null) {
- display = stack.getDisplay();
- if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
- int windowingMode = launchParams != null ? launchParams.mWindowingMode
- : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = display.resolveWindowingMode(r, options, candidateTask,
- activityType);
- }
- if (stack.isCompatible(windowingMode, activityType)) {
- return stack;
- }
- if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
- && display.getSplitScreenPrimaryStack() == stack
- && candidateTask == stack.topTask()) {
- // This is a special case when we try to launch an activity that is currently on
- // top of split-screen primary stack, but is targeting split-screen secondary.
- // In this case we don't want to move it to another stack.
- // TODO(b/78788972): Remove after differentiating between preferred and required
- // launch options.
- return stack;
- }
- }
- }
-
- if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
- display = getDefaultDisplay();
- }
-
- return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
- }
-
- /** @return true if activity record is null or can be launched on provided display. */
- private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
- if (r == null) {
- return true;
- }
- return r.canBeLaunchedOnDisplay(displayId);
- }
-
- /**
- * Get a topmost stack on the display, that is a valid launch stack for specified activity.
- * If there is no such stack, new dynamic stack can be created.
- * @param displayId Target display.
- * @param r Activity that should be launched there.
- * @param candidateTask The possible task the activity might be put in.
- * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
- */
- private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException(
- "Display with displayId=" + displayId + " not found.");
- }
-
- if (!r.canBeLaunchedOnDisplay(displayId)) {
- return null;
- }
-
- // If {@code r} is already in target display and its task is the same as the candidate task,
- // the intention should be getting a launch stack for the reusable activity, so we can use
- // the existing stack.
- if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
- return candidateTask.getStack();
- }
-
- // Return the topmost valid stack on the display.
- for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = activityDisplay.getChildAt(i);
- if (isValidLaunchStack(stack, displayId, r)) {
- return stack;
- }
- }
-
- // If there is no valid stack on the external display - check if new dynamic stack will do.
- if (displayId != DEFAULT_DISPLAY) {
- final int windowingMode;
- if (launchParams != null) {
- // When launch params is not null, we always defer to its windowing mode. Sometimes
- // it could be unspecified, which indicates it should inherit windowing mode from
- // display.
- windowingMode = launchParams.mWindowingMode;
- } else {
- windowingMode = options != null ? options.getLaunchWindowingMode()
- : r.getWindowingMode();
- }
- return activityDisplay.createStack(
- windowingMode,
- options != null ? options.getLaunchActivityType() : r.getActivityType(),
- true /*onTop*/);
- }
-
- Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
- return null;
- }
-
- ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable ActivityOptions options,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
- launchParams);
- }
-
- // TODO: Can probably be consolidated into getLaunchStack()...
- private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
- switch (stack.getActivityType()) {
- case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
- case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
- case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
- }
- // There is a 1-to-1 relationship between stack and task when not in
- // primary split-windowing mode.
- if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return false;
- } else {
- return r.supportsSplitScreenWindowingMode();
- }
- }
-
- /**
- * Get next focusable stack in the system. This will search through the stack on the same
- * display as the current focused stack, looking for a focusable and visible stack, different
- * from the target stack. If no valid candidates will be found, it will then go through all
- * displays and stacks in last-focused order.
- *
- * @param currentFocus The stack that previously had focus.
- * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
- * candidate.
- * @return Next focusable {@link ActivityStack}, {@code null} if not found.
- */
- ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
- boolean ignoreCurrent) {
- // First look for next focusable stack on the same display
- final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
- final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
- currentFocus, ignoreCurrent);
- if (preferredFocusableStack != null) {
- return preferredFocusableStack;
- }
- if (preferredDisplay.supportsSystemDecorations()) {
- // Stop looking for focusable stack on other displays because the preferred display
- // supports system decorations. Home activity would be launched on the same display if
- // no focusable stack found.
- return null;
- }
-
- // Now look through all displays
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- if (display == preferredDisplay) {
- // We've already checked this one
- continue;
- }
- final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
- ignoreCurrent);
- if (nextFocusableStack != null) {
- return nextFocusableStack;
- }
- }
-
- return null;
- }
-
- /**
- * Get next valid stack for launching provided activity in the system. This will search across
- * displays and stacks in last-focused order for a focusable and visible stack, except those
- * that are on a currently focused display.
- *
- * @param r The activity that is being launched.
- * @param currentFocus The display that previously had focus and thus needs to be ignored when
- * searching for the next candidate.
- * @return Next valid {@link ActivityStack}, null if not found.
- */
- ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- if (display.mDisplayId == currentFocus) {
- continue;
- }
- final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
- null /* options */, null /* launchParams */);
- if (stack != null) {
- return stack;
- }
- }
- return null;
- }
-
- ActivityRecord getDefaultDisplayHomeActivity() {
- return getDefaultDisplayHomeActivityForUser(mCurrentUser);
- }
-
- ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
- return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
- }
-
- void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
- Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
- boolean deferResume) {
-
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
- preserveWindows, deferResume);
- return;
- }
-
- final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
- if (!allowResizeInDockedMode
- && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
- // If the docked stack exists, don't resize non-floating stacks independently of the
- // size computed from the docked stack size (otherwise they will be out of sync)
- return;
- }
-
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
- mWindowManager.deferSurfaceLayout();
- try {
- if (stack.affectedBySplitScreenResize()) {
- if (bounds == null && stack.inSplitScreenWindowingMode()) {
- // null bounds = fullscreen windowing mode...at least for now.
- stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- } else if (splitScreenActive) {
- // If we are in split-screen mode and this stack support split-screen, then
- // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
- stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- }
- }
- stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
- if (!deferResume) {
- stack.ensureVisibleActivitiesConfigurationLocked(
- stack.topRunningActivityLocked(), preserveWindows);
- }
- } finally {
- mWindowManager.continueSurfaceLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
- }
- }
-
- void deferUpdateRecentsHomeStackBounds() {
- deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
- deferUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- void deferUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.deferUpdateBounds();
- }
- }
-
- void continueUpdateRecentsHomeStackBounds() {
- continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
- continueUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- void continueUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.continueUpdateBounds();
- }
+ private void continueUpdateRecentsHomeStackBounds() {
+ mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
}
void notifyAppTransitionDone() {
continueUpdateRecentsHomeStackBounds();
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
- final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
+ final TaskRecord task =
+ mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
if (task != null) {
task.setTaskDockedResizing(false);
}
@@ -2765,7 +1467,8 @@
try {
final int windowingMode = fromStack.getWindowingMode();
final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
- final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+ final ActivityDisplay toDisplay =
+ mRootActivityContainer.getActivityDisplay(toDisplayId);
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// Tell the display we are exiting split-screen mode.
@@ -2822,8 +1525,8 @@
}
}
- ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
@@ -2869,7 +1572,7 @@
false /* deferResume */);
}
- private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows, boolean deferResume) {
@@ -2878,7 +1581,8 @@
return;
}
- final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack stack =
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
return;
@@ -2917,7 +1621,7 @@
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
- final ActivityDisplay display = getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final Rect otherTaskRect = new Rect();
for (int i = display.getChildCount() - 1; i >= 0; --i) {
final ActivityStack current = display.getChildAt(i);
@@ -2937,7 +1641,8 @@
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */);
- resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
+ mRootActivityContainer.resizeStack(current,
+ !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2955,7 +1660,8 @@
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
// TODO(multi-display): Pinned stack display should be passed in.
- final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
+ final PinnedActivityStack stack =
+ mRootActivityContainer.getDefaultDisplay().getPinnedStack();
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
@@ -3036,22 +1742,6 @@
}
/**
- * Removes stacks in the input windowing modes from the system if they are of activity type
- * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
- */
- void removeStacksInWindowingModes(int... windowingModes) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
- }
- }
-
- void removeStacksWithActivityTypes(int... activityTypes) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
- }
- }
-
- /**
* See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
@@ -3072,7 +1762,8 @@
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
boolean pauseImmediately, String reason) {
- final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ final TaskRecord tr =
+ mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
tr.removeTaskActivitiesLocked(pauseImmediately, reason);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
@@ -3161,7 +1852,8 @@
* @return true if the task has been restored successfully.
*/
boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
- final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop);
+ final ActivityStack stack =
+ mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop);
final ActivityStack currentStack = task.getStack();
if (currentStack != null) {
// Task has already been restored once. See if we need to do anything more
@@ -3181,7 +1873,7 @@
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- activities.get(activityNdx).createWindowContainer();
+ activities.get(activityNdx).createAppWindowToken();
}
return true;
}
@@ -3203,39 +1895,6 @@
}
/**
- * Move stack with all its existing content to specified display.
- * @param stackId Id of stack to move.
- * @param displayId Id of display to move stack to.
- * @param onTop Indicates whether container should be place on top or on bottom.
- */
- void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
- + displayId);
- }
- final ActivityStack stack = getStack(stackId);
- if (stack == null) {
- throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
- + stackId);
- }
-
- final ActivityDisplay currentDisplay = stack.getDisplay();
- if (currentDisplay == null) {
- throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack
- + " is not attached to any display.");
- }
-
- if (currentDisplay.mDisplayId == displayId) {
- throw new IllegalArgumentException("Trying to move stack=" + stack
- + " to its current displayId=" + displayId);
- }
-
- stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
- // TODO(multi-display): resize stacks properly if moved from split-screen.
- }
-
- /**
* Returns the reparent target stack, creating the stack if necessary. This call also enforces
* the various checks on tasks that are going to be reparented from one stack to another.
*/
@@ -3287,159 +1946,6 @@
return stack;
}
- boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
- final ActivityStack stack = getStack(stackId);
- if (stack == null) {
- throw new IllegalArgumentException(
- "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
- }
-
- final ActivityRecord r = stack.topRunningActivityLocked();
- if (r == null) {
- Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
- + " in stack=" + stack);
- return false;
- }
-
- if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
- Slog.w(TAG,
- "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
- + " r=" + r);
- return false;
- }
-
- moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
- "moveTopActivityToPinnedStack");
- return true;
- }
-
- void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
- String reason) {
-
- mWindowManager.deferSurfaceLayout();
-
- final ActivityDisplay display = r.getStack().getDisplay();
- PinnedActivityStack stack = display.getPinnedStack();
-
- // This will clear the pinned stack by moving an existing task to the full screen stack,
- // ensuring only one task is present.
- if (stack != null) {
- moveTasksToFullscreenStackLocked(stack, !ON_TOP);
- }
-
- // Need to make sure the pinned stack exist so we can resize it below...
- stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
-
- // Calculate the target bounds here before the task is reparented back into pinned windowing
- // mode (which will reset the saved bounds)
- final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
- try {
- final TaskRecord task = r.getTask();
- // 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.
- resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
- true /* allowResizeInDockedMode */, !DEFER_RESUME);
-
- if (task.mActivities.size() == 1) {
- // Defer resume until below, and do not schedule PiP changes until we animate below
- task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
- false /* schedulePictureInPictureModeChange */, reason);
- } else {
- // There are multiple activities in the task and moving the top activity should
- // reveal/leave the other activities in their original task.
-
- // Currently, we don't support reparenting activities across tasks in two different
- // stacks, so instead, just create a new task in the same stack, reparent the
- // activity into that task, and then reparent the whole task to the new stack. This
- // ensures that all the necessary work to migrate states in the old and new stacks
- // is also done.
- final TaskRecord newTask = task.getStack().createTaskRecord(
- getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
- r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
-
- // Defer resume until below, and do not schedule PiP changes until we animate below
- newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
- DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
- }
-
- // Reset the state that indicates it can enter PiP while pausing after we've moved it
- // to the pinned stack
- r.supportsEnterPipOnTaskSwitch = false;
- } finally {
- mWindowManager.continueSurfaceLayout();
- }
-
- stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
- true /* fromFullscreen */);
-
- // Update the visibility of all activities after the they have been reparented to the new
- // stack. This MUST run after the animation above is scheduled to ensure that the windows
- // drawn signal is scheduled after the bounds animation start call on the bounds animator
- // thread.
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- resumeFocusedStacksTopActivitiesLocked();
-
- mService.getTaskChangeNotificationController().notifyActivityPinned(r);
- }
-
- ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
- mTmpFindTaskResult.clear();
-
- // Looking up task on preferred display first
- final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
- if (preferredDisplay != null) {
- preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
- }
- }
-
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- if (display.mDisplayId == preferredDisplayId) {
- continue;
- }
-
- display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
- }
- }
-
- if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
- return mTmpFindTaskResult.mRecord;
- }
-
- ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
- boolean compareIntentFilters) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord ar = stack.findActivityLocked(
- intent, info, compareIntentFilters);
- if (ar != null) {
- return ar;
- }
- }
- }
- return null;
- }
-
- boolean hasAwakeDisplay() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- if (!display.shouldSleep()) {
- return true;
- }
- }
- return false;
- }
-
void goingToSleepLocked() {
scheduleSleepTimeout();
if (!mGoingToSleep.isHeld()) {
@@ -3453,24 +1959,19 @@
}
}
- applySleepTokensLocked(false /* applyToStacks */);
+ mRootActivityContainer.applySleepTokens(false /* applyToStacks */);
checkReadyForSleepLocked(true /* allowDelay */);
}
- void prepareForShutdownLocked() {
- for (int i = 0; i < mActivityDisplays.size(); i++) {
- createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
- }
- }
-
boolean shutdownLocked(int timeout) {
goingToSleepLocked();
boolean timedout = false;
final long endTime = System.currentTimeMillis() + timeout;
while (true) {
- if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) {
+ if (!mRootActivityContainer.putStacksToSleep(
+ true /* allowDelay */, true /* shuttingDown */)) {
long timeRemaining = endTime - System.currentTimeMillis();
if (timeRemaining > 0) {
try {
@@ -3500,54 +2001,9 @@
}
}
- void applySleepTokensLocked(boolean applyToStacks) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- // Set the sleeping state of the display.
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final boolean displayShouldSleep = display.shouldSleep();
- if (displayShouldSleep == display.isSleeping()) {
- continue;
- }
- display.setIsSleeping(displayShouldSleep);
-
- if (!applyToStacks) {
- continue;
- }
-
- // Set the sleeping state of the stacks on the display.
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (displayShouldSleep) {
- stack.goToSleepIfPossible(false /* shuttingDown */);
- } else {
- stack.awakeFromSleepingLocked();
- if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
- .isKeyguardOrAodShowing(display.mDisplayId)) {
- // If the keyguard is unlocked - resume immediately.
- // It is possible that the display will not be awake at the time we
- // process the keyguard going away, which can happen before the sleep token
- // is released. As a result, it is important we resume the activity here.
- resumeFocusedStacksTopActivitiesLocked();
- }
- }
- }
-
- if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
- continue;
- }
- // The display is awake now, so clean up the going to sleep list.
- for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
- final ActivityRecord r = it.next();
- if (r.getDisplayId() == display.mDisplayId) {
- it.remove();
- }
- }
- }
- }
-
void activitySleptLocked(ActivityRecord r) {
mGoingToSleepActivities.remove(r);
- final ActivityStack s = r.getStack();
+ final ActivityStack s = r.getActivityStack();
if (s != null) {
s.checkReadyForSleep();
} else {
@@ -3561,12 +2017,13 @@
return;
}
- if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) {
+ if (!mRootActivityContainer.putStacksToSleep(
+ allowDelay, false /* shuttingDown */)) {
return;
}
// Send launch end powerhint before going sleep
- sendPowerHintForLaunchEndIfNeeded();
+ mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
removeSleepTimeouts();
@@ -3578,55 +2035,27 @@
}
}
- // Tries to put all activity stacks to sleep. Returns true if all stacks were
- // successfully put to sleep.
- private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
- boolean allSleep = true;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (allowDelay) {
- allSleep &= stack.goToSleepIfPossible(shuttingDown);
- } else {
- stack.goToSleep();
- }
- }
- }
- return allSleep;
- }
-
boolean reportResumedActivityLocked(ActivityRecord r) {
// A resumed activity cannot be stopping. remove from list
mStoppingActivities.remove(r);
- final ActivityStack stack = r.getStack();
- if (isTopDisplayFocusedStack(stack)) {
+ final ActivityStack stack = r.getActivityStack();
+ if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
mService.updateUsageStats(r, true);
}
if (stack.getDisplay().allResumedActivitiesComplete()) {
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
// for all displays in this stage.
- executeAppTransitionForAllDisplay();
+ mRootActivityContainer.executeAppTransitionForAllDisplay();
return true;
}
return false;
}
- void handleAppCrashLocked(WindowProcessController app) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.handleAppCrashLocked(app);
- }
- }
- }
-
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
final ActivityStack stack = task.getStack();
r.mLaunchTaskBehind = false;
@@ -3638,7 +2067,7 @@
// task has been shown briefly
final ActivityRecord top = stack.getTopActivity();
if (top != null) {
- top.getTask().touchActiveTime();
+ top.getTaskRecord().touchActiveTime();
}
}
@@ -3646,157 +2075,9 @@
mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
}
- /**
- * Make sure that all activities that need to be visible in the system actually are and update
- * their configuration.
- */
- void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
- boolean preserveWindows) {
- ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
- true /* notifyClients */);
- }
-
- /**
- * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
- */
- void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
- getKeyguardController().beginActivityVisibilityUpdate();
- try {
- // First the front stacks. In case any are not fullscreen and are in front of home.
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
- notifyClients);
- }
- }
- } finally {
- getKeyguardController().endActivityVisibilityUpdate();
- }
- }
-
- void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.addStartingWindowsForVisibleActivities(taskSwitch);
- }
- }
- }
-
- void invalidateTaskLayers() {
- mTaskLayersChanged = true;
- }
-
- void rankTaskLayersIfNeeded() {
- if (!mTaskLayersChanged) {
- return;
- }
- mTaskLayersChanged = false;
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- int baseLayer = 0;
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- baseLayer += stack.rankTaskLayers(baseLayer);
- }
- }
- }
-
- void clearOtherAppTimeTrackers(AppTimeTracker except) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.clearOtherAppTimeTrackers(except);
- }
- }
- }
-
- void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.scheduleDestroyActivities(app, reason);
- }
- }
- }
-
- void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
- // Tasks is non-null only if two or more tasks are found.
- ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
- if (tasks == null) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
- return;
- }
- // If we have activities in multiple tasks that are in a position to be destroyed,
- // let's iterate through the tasks and release the oldest one.
- final int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final int stackCount = display.getChildCount();
- // Step through all stacks starting from behind, to hit the oldest things first.
- for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- // Try to release activities in this stack; if we manage to, we are done.
- if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
- return;
- }
- }
- }
- }
-
- boolean switchUserLocked(int userId, UserState uss) {
- final int focusStackId = getTopDisplayFocusedStack().getStackId();
- // We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
- if (dockedStack != null) {
- moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
- }
- // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
- // also cause all tasks to be moved to the fullscreen stack at a position that is
- // appropriate.
- removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
- mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId =
- mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
- mCurrentUser = userId;
-
- mStartingUsers.add(uss);
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.switchUserLocked(userId);
- TaskRecord task = stack.topTask();
- if (task != null) {
- stack.positionChildWindowContainerAtTop(task);
- }
- }
- }
-
- ActivityStack stack = getStack(restoreStackId);
- if (stack == null) {
- stack = getDefaultDisplay().getHomeStack();
- }
- final boolean homeInFront = stack.isActivityTypeHome();
- if (stack.isOnHomeDisplay()) {
- stack.moveToFront("switchUserOnHomeDisplay");
- } else {
- // Stack was moved to another display while user was swapped out.
- resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
- }
- return homeInFront;
- }
-
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
- if (userId == mCurrentUser) return true;
+ if (userId == mRootActivityContainer.mCurrentUser) return true;
return mService.mAmInternal.isCurrentProfile(userId);
}
@@ -3821,7 +2102,7 @@
boolean remove, boolean processPausingActivities) {
ArrayList<ActivityRecord> stops = null;
- final boolean nowVisible = allResumedActivitiesVisible();
+ final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible();
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s);
@@ -3841,7 +2122,7 @@
}
}
if (remove) {
- final ActivityStack stack = s.getStack();
+ final ActivityStack stack = s.getActivityStack();
final boolean shouldSleepOrShutDown = stack != null
? stack.shouldSleepOrShutDownActivities()
: mService.isSleepingOrShuttingDownLocked();
@@ -3871,134 +2152,26 @@
return stops;
}
- void validateTopActivitiesLocked() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.topRunningActivityLocked();
- final ActivityState state = r == null ? DESTROYED : r.getState();
- if (isTopDisplayFocusedStack(stack)) {
- if (r == null) Slog.e(TAG,
- "validateTop...: null top activity, stack=" + stack);
- else {
- final ActivityRecord pausing = stack.mPausingActivity;
- if (pausing != null && pausing == r) Slog.e(TAG,
- "validateTop...: top stack has pausing activity r=" + r
- + " state=" + state);
- if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
- "validateTop...: activity in front not resumed r=" + r
- + " state=" + state);
- }
- } else {
- final ActivityRecord resumed = stack.getResumedActivity();
- if (resumed != null && resumed == r) Slog.e(TAG,
- "validateTop...: back stack has resumed activity r=" + r
- + " state=" + state);
- if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
- "validateTop...: activity in back resumed r=" + r + " state=" + state);
- }
- }
- }
- }
-
- public void dumpDisplays(PrintWriter pw) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- pw.print("[id:" + display.mDisplayId + " stacks:");
- display.dumpStacks(pw);
- pw.print("]");
- }
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.println();
pw.println("ActivityStackSupervisor state:");
- pw.print(prefix);
- pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+ mRootActivityContainer.dump(pw, prefix);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
- pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- display.dump(pw, prefix);
- }
+ pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront);
if (!mWaitingForActivityVisible.isEmpty()) {
- pw.print(prefix); pw.println("mWaitingForActivityVisible=");
+ pw.println(prefix + "mWaitingForActivityVisible=");
for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
- pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
+ pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
}
}
pw.print(prefix); pw.print("isHomeRecentsComponent=");
- pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser));
getKeyguardController().dump(pw, prefix);
mService.getLockTaskController().dump(pw, prefix);
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- activityDisplay.writeToProto(proto, DISPLAYS);
- }
- getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
- // TODO(b/111541062): Update tests to look for resumed activities on all displays
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- if (focusedStack != null) {
- proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
- final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
- if (focusedActivity != null) {
- focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
- }
- } else {
- proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
- }
- proto.write(IS_HOME_RECENTS_COMPONENT,
- mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
- mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
- proto.end(token);
- }
-
- /**
- * Dump all connected displays' configurations.
- * @param prefix Prefix to apply to each line of the dump.
- */
- void dumpDisplayConfigs(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.println("Display override configurations:");
- final int displayCount = mActivityDisplays.size();
- 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());
- }
- }
-
- /**
- * Dumps the activities matching the given {@param name} in the either the focused stack
- * or all visible stacks if {@param dumpVisibleStacks} is true.
- */
- ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
- boolean dumpFocusedStackOnly) {
- if (dumpFocusedStackOnly) {
- return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
- } else {
- ArrayList<ActivityRecord> activities = new ArrayList<>();
- int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
- activities.addAll(stack.getDumpActivitiesLocked(name));
- }
- }
- }
- return activities;
- }
- }
-
static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
boolean needSep, String prefix) {
if (activity != null) {
@@ -4014,73 +2187,6 @@
return false;
}
- boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage) {
- boolean printed = false;
- boolean needSep = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
- pw.println(" (activities from top to bottom):");
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- pw.println();
- pw.println(" Stack #" + stack.mStackId
- + ": type=" + activityTypeToString(stack.getActivityType())
- + " mode=" + windowingModeToString(stack.getWindowingMode()));
- pw.println(" isSleeping=" + stack.shouldSleepActivities());
- pw.println(" mBounds=" + stack.getOverrideBounds());
-
- printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
- needSep);
-
- printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false,
- !dumpAll, false, dumpPackage, true,
- " Running activities (most recent first):", null);
-
- needSep = printed;
- boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
- " mPausingActivity: ");
- if (pr) {
- printed = true;
- needSep = false;
- }
- pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
- " mResumedActivity: ");
- if (pr) {
- printed = true;
- needSep = false;
- }
- if (dumpAll) {
- pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
- " mLastPausedActivity: ");
- if (pr) {
- printed = true;
- needSep = true;
- }
- printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
- needSep, " mLastNoHistoryActivity: ");
- }
- needSep = printed;
- }
- printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
- " ResumedActivity:");
- }
-
- printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to finish:", null);
- printed |= dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to stop:", null);
- printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, " ", "Wait",
- false, !dumpAll, false, dumpPackage, true,
- " Activities waiting for another to become visible:", null);
- printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to sleep:", null);
-
- return printed;
- }
-
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
@@ -4106,8 +2212,8 @@
pw.println(header);
header = null;
}
- if (lastTask != r.getTask()) {
- lastTask = r.getTask();
+ if (lastTask != r.getTaskRecord()) {
+ lastTask = r.getTaskRecord();
pw.print(prefix);
pw.print(full ? "* " : " ");
pw.println(lastTask);
@@ -4190,294 +2296,6 @@
mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
}
- @Override
- public void onDisplayAdded(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
- synchronized (mService.mGlobalLock) {
- getActivityDisplayOrCreateLocked(displayId);
- // Do not start home before booting, or it may accidentally finish booting before it
- // starts. Instead, we expect home activities to be launched when the system is ready
- // (ActivityManagerService#systemReady).
- if (mService.isBooted() || mService.isBooting()) {
- startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
- }
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
- if (displayId == DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can't remove the primary display.");
- }
-
- synchronized (mService.mGlobalLock) {
- final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
- if (activityDisplay == null) {
- return;
- }
-
- activityDisplay.remove();
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
- synchronized (mService.mGlobalLock) {
- final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
- if (activityDisplay != null) {
- activityDisplay.onDisplayChanged();
- }
- }
- }
-
- /** Check if display with specified id is added to the list. */
- boolean isDisplayAdded(int displayId) {
- return getActivityDisplayOrCreateLocked(displayId) != null;
- }
-
- // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
- ActivityDisplay getActivityDisplay(int displayId) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
- if (activityDisplay.mDisplayId == displayId) {
- return activityDisplay;
- }
- }
- return null;
- }
-
- // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
- ActivityDisplay getDefaultDisplay() {
- return mDefaultDisplay;
- }
-
- /**
- * Get an existing instance of {@link ActivityDisplay} or create new if there is a
- * corresponding record in display manager.
- */
- // TODO: Look into consolidating with getActivityDisplay()
- ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
- ActivityDisplay activityDisplay = getActivityDisplay(displayId);
- if (activityDisplay != null) {
- return activityDisplay;
- }
- if (mDisplayManager == null) {
- // The system isn't fully initialized yet.
- return null;
- }
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- // The display is not registered in DisplayManager.
- return null;
- }
- // The display hasn't been added to ActivityManager yet, create a new record now.
- activityDisplay = new ActivityDisplay(this, display);
- addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
- return activityDisplay;
- }
-
- /**
- * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
- * defined in {@link DisplayInfo#uniqueId}.
- *
- * @param uniqueId the unique ID of the display
- * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
- */
- ActivityDisplay getActivityDisplay(String uniqueId) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- final boolean isValid = display.mDisplay.isValid();
- if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
- return display;
- }
- }
-
- return null;
- }
-
- boolean startHomeOnAllDisplays(int userId, String reason) {
- boolean homeStarted = false;
- for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
- final int displayId = mActivityDisplays.get(i).mDisplayId;
- homeStarted |= startHomeOnDisplay(userId, reason, displayId);
- }
- return homeStarted;
- }
-
- /**
- * This starts home activity on displays that can have system decorations and only if the
- * home activity can have multiple instances.
- */
- boolean startHomeOnDisplay(int userId, String reason, int displayId) {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
- if (aInfo == null) {
- return false;
- }
-
- if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
- return false;
- }
-
- // Update the reason for ANR debugging to verify if the user activity is the one that
- // actually launched.
- final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
- aInfo.applicationInfo.uid);
- mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
- displayId);
- return true;
- }
-
- /**
- * This resolves the home activity info and updates the home component of the given intent.
- * @return the home activity info if any.
- */
- private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
- final int flags = ActivityManagerService.STOCK_PM_FLAGS;
- final ComponentName comp = homeIntent.getComponent();
- ActivityInfo aInfo = null;
- try {
- if (comp != null) {
- // Factory test.
- aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
- } else {
- final String resolvedType =
- homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
- final ResolveInfo info = AppGlobals.getPackageManager()
- .resolveIntent(homeIntent, resolvedType, flags, userId);
- if (info != null) {
- aInfo = info.activityInfo;
- }
- }
- } catch (RemoteException e) {
- // ignore
- }
-
- if (aInfo == null) {
- Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
- return null;
- }
-
- homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
- aInfo = new ActivityInfo(aInfo);
- aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
- homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
- return aInfo;
- }
-
- @VisibleForTesting
- void addChild(ActivityDisplay activityDisplay, int position) {
- positionChildAt(activityDisplay, position);
- mWindowContainerController.positionChildAt(
- activityDisplay.getWindowContainerController(), position);
- }
-
- void removeChild(ActivityDisplay activityDisplay) {
- // The caller must tell the controller of {@link ActivityDisplay} to release its container
- // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
- mActivityDisplays.remove(activityDisplay);
- }
-
- private void calculateDefaultMinimalSizeOfResizeableTasks() {
- final Resources res = mService.mContext.getResources();
- final float minimalSize = res.getDimension(
- com.android.internal.R.dimen.default_minimal_size_resizable_task);
- final DisplayMetrics dm = res.getDisplayMetrics();
-
- mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
- }
-
- SleepToken createSleepTokenLocked(String tag, int displayId) {
- final ActivityDisplay display = getActivityDisplay(displayId);
- if (display == null) {
- throw new IllegalArgumentException("Invalid display: " + displayId);
- }
-
- final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
- mSleepTokens.add(token);
- display.mAllSleepTokens.add(token);
- return token;
- }
-
- private void removeSleepTokenLocked(SleepTokenImpl token) {
- mSleepTokens.remove(token);
-
- final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
- if (display != null) {
- display.mAllSleepTokens.remove(token);
- if (display.mAllSleepTokens.isEmpty()) {
- mService.updateSleepIfNeededLocked();
- }
- }
- }
-
- private StackInfo getStackInfo(ActivityStack stack) {
- final int displayId = stack.mDisplayId;
- final ActivityDisplay display = getActivityDisplay(displayId);
- StackInfo info = new StackInfo();
- stack.getWindowContainerBounds(info.bounds);
- info.displayId = displayId;
- info.stackId = stack.mStackId;
- info.userId = stack.mCurrentUser;
- info.visible = stack.shouldBeVisible(null);
- // A stack might be not attached to a display.
- info.position = display != null ? display.getIndexOf(stack) : 0;
- info.configuration.setTo(stack.getConfiguration());
-
- ArrayList<TaskRecord> tasks = stack.getAllTasks();
- final int numTasks = tasks.size();
- int[] taskIds = new int[numTasks];
- String[] taskNames = new String[numTasks];
- Rect[] taskBounds = new Rect[numTasks];
- int[] taskUserIds = new int[numTasks];
- for (int i = 0; i < numTasks; ++i) {
- final TaskRecord task = tasks.get(i);
- taskIds[i] = task.taskId;
- taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
- : task.realActivity != null ? task.realActivity.flattenToString()
- : task.getTopActivity() != null ? task.getTopActivity().packageName
- : "unknown";
- taskBounds[i] = new Rect();
- task.getWindowContainerBounds(taskBounds[i]);
- taskUserIds[i] = task.userId;
- }
- info.taskIds = taskIds;
- info.taskNames = taskNames;
- info.taskBounds = taskBounds;
- info.taskUserIds = taskUserIds;
-
- final ActivityRecord top = stack.topRunningActivityLocked();
- info.topActivity = top != null ? top.intent.getComponent() : null;
- return info;
- }
-
- StackInfo getStackInfo(int stackId) {
- ActivityStack stack = getStack(stackId);
- if (stack != null) {
- return getStackInfo(stack);
- }
- return null;
- }
-
- StackInfo getStackInfo(int windowingMode, int activityType) {
- final ActivityStack stack = getStack(windowingMode, activityType);
- return (stack != null) ? getStackInfo(stack) : null;
- }
-
- ArrayList<StackInfo> getAllStackInfosLocked() {
- ArrayList<StackInfo> list = new ArrayList<>();
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- list.add(getStackInfo(stack));
- }
- }
- return list;
- }
-
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
int preferredDisplayId, ActivityStack actualStack) {
handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
@@ -4552,7 +2370,7 @@
mWindowManager.notifyAppRelaunchingFinished(token);
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- if (r.getStack().shouldSleepOrShutDownActivities()) {
+ if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
r.setSleeping(true, true);
}
}
@@ -4592,7 +2410,7 @@
return;
}
- scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getOverrideBounds());
+ scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getRequestedOverrideBounds());
}
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
@@ -4623,21 +2441,6 @@
}
}
- void setDockedStackMinimized(boolean minimized) {
- // Get currently focused stack before setting mIsDockMinimized. We do this because if
- // split-screen is active, primary stack will not be focusable (see #isFocusable) while
- // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
- final ActivityStack current = getTopDisplayFocusedStack();
- mIsDockMinimized = minimized;
- if (mIsDockMinimized) {
- if (current.inSplitScreenPrimaryWindowingMode()) {
- // The primary split-screen stack can't be focused while it is minimize, so move
- // focus to something else.
- current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
- }
- }
- }
-
void wakeUp(String reason) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
}
@@ -4656,10 +2459,8 @@
mDeferResumeCount--;
}
- /**
- * @return True if resume can be called.
- */
- private boolean readyToResume() {
+ /** @return True if resume can be called. */
+ boolean readyToResume() {
return mDeferResumeCount == 0;
}
@@ -4711,7 +2512,7 @@
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService.mGlobalLock) {
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
} break;
case SLEEP_TIMEOUT_MSG: {
@@ -4747,19 +2548,6 @@
}
}
- ActivityStack findStackBehind(ActivityStack stack) {
- final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
- if (display != null) {
- for (int i = display.getChildCount() - 1; i >= 0; i--) {
- if (display.getChildAt(i) == stack && i > 0) {
- return display.getChildAt(i - 1);
- }
- }
- }
- throw new IllegalStateException("Failed to find a stack behind stack=" + stack
- + " in=" + display);
- }
-
/**
* Puts a task into resizing mode during the next app transition.
*
@@ -4804,8 +2592,8 @@
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
- task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
- activityOptions, ON_TOP);
+ task = mRootActivityContainer.anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
if (task == null) {
continueUpdateRecentsHomeStackBounds();
mWindowManager.executeAppTransition();
@@ -4818,7 +2606,8 @@
// from whatever is started from the recents activity, so move the home stack
// forward.
// TODO (b/115289124): Multi-display supports for recents.
- getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
+ mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront(
+ "startActivityFromRecents");
}
// If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4827,7 +2616,8 @@
&& task.getRootActivity() != null) {
final ActivityRecord targetActivity = task.getTopActivity();
- sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ true /* forceSend */, targetActivity);
mActivityMetricsLogger.notifyActivityLaunching(task.intent);
try {
mService.moveTaskToFrontLocked(task.taskId, 0, options,
@@ -4880,35 +2670,6 @@
}
/**
- * @return a list of activities which are the top ones in each visible stack. The first
- * entry will be the focused activity.
- */
- List<IBinder> getTopVisibleActivities() {
- final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
- // Traverse all displays.
- for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- // Traverse all stacks on a display.
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
- // Get top activity from a visible stack and add it to the list.
- if (stack.shouldBeVisible(null /* starting */)) {
- final ActivityRecord top = stack.getTopActivity();
- if (top != null) {
- if (stack == topFocusedStack) {
- topActivityTokens.add(0, top.appToken);
- } else {
- topActivityTokens.add(top.appToken);
- }
- }
- }
- }
- }
- return topActivityTokens;
- }
-
- /**
* Internal container to store a match qualifier alongside a WaitResult.
*/
static class WaitInfo {
@@ -4946,30 +2707,4 @@
mResult.dump(pw, prefix);
}
}
-
- private final class SleepTokenImpl extends SleepToken {
- private final String mTag;
- private final long mAcquireTime;
- private final int mDisplayId;
-
- public SleepTokenImpl(String tag, int displayId) {
- mTag = tag;
- mDisplayId = displayId;
- mAcquireTime = SystemClock.uptimeMillis();
- }
-
- @Override
- public void release() {
- synchronized (mService.mGlobalLock) {
- removeSleepTokenLocked(this);
- }
- }
-
- @Override
- public String toString() {
- return "{\"" + mTag + "\", display " + mDisplayId
- + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
- }
- }
-
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index ee5a43c..b8442a8 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -70,6 +70,7 @@
private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
+ private final RootActivityContainer mRootActivityContainer;
private final Context mServiceContext;
// UserManager cannot be final as it's not ready when this class is instantiated during boot
@@ -102,14 +103,15 @@
ActivityStartInterceptor(
ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
- this(service, supervisor, service.mContext);
+ this(service, supervisor, service.mRootActivityContainer, service.mContext);
}
@VisibleForTesting
ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
- Context context) {
+ RootActivityContainer root, Context context) {
mService = service;
mSupervisor = supervisor;
+ mRootActivityContainer = root;
mServiceContext = context;
}
@@ -279,11 +281,11 @@
mActivityOptions = ActivityOptions.makeBasic();
}
- ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
- if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
- // Showing credential confirmation activity in home task to avoid stopping multi-windowed
- // mode after showing the full-screen credential confirmation activity.
- mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId);
+ ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity();
+ if (homeActivityRecord != null && homeActivityRecord.getTaskRecord() != null) {
+ // Showing credential confirmation activity in home task to avoid stopping
+ // multi-windowed mode after showing the full-screen credential confirmation activity.
+ mActivityOptions.setLaunchTaskId(homeActivityRecord.getTaskRecord().taskId);
}
final UserInfo parent = mUserManager.getProfileParent(mUserId);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d4c1bca..bc2136e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -108,6 +109,7 @@
import android.util.EventLog;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
+import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
@@ -137,6 +139,7 @@
private static final int INVALID_LAUNCH_MODE = -1;
private final ActivityTaskManagerService mService;
+ private final RootActivityContainer mRootActivityContainer;
private final ActivityStackSupervisor mSupervisor;
private final ActivityStartInterceptor mInterceptor;
private final ActivityStartController mController;
@@ -421,6 +424,7 @@
ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
mController = controller;
mService = service;
+ mRootActivityContainer = service.mRootActivityContainer;
mSupervisor = supervisor;
mInterceptor = interceptor;
reset(true);
@@ -617,7 +621,7 @@
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
- sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
+ sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Will send result to " + resultTo + " " + sourceRecord);
if (sourceRecord != null) {
@@ -674,7 +678,7 @@
}
if (err == ActivityManager.START_SUCCESS && sourceRecord != null
- && sourceRecord.getTask().voiceSession != null) {
+ && sourceRecord.getTaskRecord().voiceSession != null) {
// If this activity is being launched as part of a voice session, we need
// to ensure that it is safe to do so. If the upcoming activity will also
// be part of the voice session, we can only launch it if it has explicitly
@@ -714,7 +718,8 @@
}
}
- final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack();
+ final ActivityStack resultStack = resultRecord == null
+ ? null : resultRecord.getActivityStack();
if (err != START_SUCCESS) {
if (resultRecord != null) {
@@ -731,6 +736,13 @@
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
+ // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
+ // on START_ABORTED
+ if (!abort) {
+ abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
+ callerApp);
+ }
+
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
@@ -774,6 +786,8 @@
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
ActivityOptions.abort(checkedOptions);
+ maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
+ null /*r*/, originatingPendingIntent, true /*abortedStart*/);
return START_ABORTED;
}
@@ -811,7 +825,8 @@
null /*profilerInfo*/);
if (DEBUG_PERMISSIONS_REVIEW) {
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack =
+ mRootActivityContainer.getTopDisplayFocusedStack();
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
true, false) + "} from uid " + callingUid + " on display "
+ (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
@@ -847,7 +862,7 @@
r.appTimeTracker = sourceRecord.appTimeTracker;
}
- final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
// If we are starting an activity that is not from the same uid as the currently resumed
// one, check whether app switches are allowed.
@@ -866,19 +881,60 @@
mController.doPendingActivityLaunches(false);
maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
- originatingPendingIntent);
+ originatingPendingIntent, false /*abortedStart*/);
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
+ private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
+ int realCallingUid, WindowProcessController callerApp) {
+ if (mService.isBackgroundActivityStartsEnabled()) {
+ return false;
+ }
+ // don't abort for the most important UIDs
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+ return false;
+ }
+ // don't abort if the callerApp has any visible activity
+ if (callerApp != null && callerApp.hasForegroundActivities()) {
+ return false;
+ }
+ // don't abort if the callingUid is in the foreground
+ if (isUidForeground(callingUid)) {
+ return false;
+ }
+ // don't abort if the realCallingUid is in the foreground and callingUid isn't
+ if ((realCallingUid != callingUid) && isUidForeground(realCallingUid)) {
+ return false;
+ }
+ // don't abort if the caller has the same uid as the recents component
+ if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+ return false;
+ }
+ // anything that has fallen through will currently be aborted
+ // TODO: remove this toast after feature development is done
+ mService.mUiHandler.post(() -> {
+ Toast.makeText(mService.mContext,
+ "Blocking background activity start for " + callingPackage,
+ Toast.LENGTH_SHORT).show();
+ });
+ return true;
+ }
+
+ /** Returns true if uid has a visible window or its process is in top or persistent state. */
+ private boolean isUidForeground(int uid) {
+ return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_TOP)
+ || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+ }
+
private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
Intent intent, WindowProcessController callerApp, ActivityRecord r,
- PendingIntentRecord originatingPendingIntent) {
+ PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
boolean callerAppHasForegroundActivity =
callerApp != null && callerApp.hasForegroundActivities();
if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
- || r == null) {
+ || (!abortedStart && r == null)) {
// skip logging in this case
return;
}
@@ -894,8 +950,8 @@
final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
? callingUidHasAnyVisibleWindow
: mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
- final String targetPackage = r.packageName;
- final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
+ final String targetPackage = (r != null) ? r.packageName : null;
+ final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
final int targetUidProcState = mService.getUidStateLocked(targetUid);
final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
@@ -1063,7 +1119,7 @@
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService.mGlobalLock) {
- final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
stack.mConfigWillChange = globalConfig != null
&& mService.getGlobalConfiguration().diff(globalConfig) != 0;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1179,7 +1235,7 @@
}
case START_DELIVERED_TO_TOP: {
outResult.timeout = false;
- outResult.who = r.realActivity;
+ outResult.who = r.mActivityComponent;
outResult.totalTime = 0;
break;
}
@@ -1188,11 +1244,12 @@
// in the resumed state.
if (r.nowVisible && r.isState(RESUMED)) {
outResult.timeout = false;
- outResult.who = r.realActivity;
+ outResult.who = r.mActivityComponent;
outResult.totalTime = 0;
} else {
final long startTimeMs = SystemClock.uptimeMillis();
- mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs);
+ mSupervisor.waitActivityVisible(
+ r.mActivityComponent, outResult, startTimeMs);
// Note: the timeout variable is not currently not ever set.
do {
try {
@@ -1238,7 +1295,7 @@
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
} finally {
- final ActivityStack currentStack = r.getStack();
+ final ActivityStack currentStack = r.getActivityStack();
startedActivityStack = currentStack != null ? currentStack : mTargetStack;
if (ActivityManager.isStartResultSuccessful(result)) {
@@ -1249,7 +1306,8 @@
final ActivityRecord currentTop =
startedActivityStack.topRunningActivityLocked();
if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
- mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+ mRootActivityContainer.ensureVisibilityAndConfig(
+ currentTop, currentTop.getDisplayId(),
true /* markFrozenIfConfigChanged */, false /* deferResume */);
}
}
@@ -1257,7 +1315,7 @@
// If we are not able to proceed, disassociate the activity from the task.
// Leaving an activity in an incomplete state can lead to issues, such as
// performing operations without a window container.
- final ActivityStack stack = mStartActivity.getStack();
+ final ActivityStack stack = mStartActivity.getActivityStack();
if (stack != null) {
stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
null /* intentResultData */, "startActivity", true /* oomAdj */);
@@ -1284,7 +1342,7 @@
// Do not start home activity if it cannot be launched on preferred display. We are not
// doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
// fallback to launch on other displays.
- if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info,
+ if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info,
mPreferredDisplayId, true /* allowInstrumenting */)) {
Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
return START_CANCELED;
@@ -1302,7 +1360,8 @@
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
// the device would otherwise leave the locked task.
- if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ reusedActivity.getTaskRecord(),
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
@@ -1319,14 +1378,14 @@
// If mStartActivity does not have a task associated with it, associate it with the
// reused activity's task. Do not do so if we're clearing top and resetting for a
// standard launchMode activity.
- if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
- mStartActivity.setTask(reusedActivity.getTask());
+ if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {
+ mStartActivity.setTask(reusedActivity.getTaskRecord());
}
- if (reusedActivity.getTask().intent == null) {
+ if (reusedActivity.getTaskRecord().intent == null) {
// This task was started because of movement of the activity based on affinity...
// Now that we are actually launching it, we can assign the base intent.
- reusedActivity.getTask().setIntent(mStartActivity);
+ reusedActivity.getTaskRecord().setIntent(mStartActivity);
}
// This code path leads to delivering a new intent, we want to make sure we schedule it
@@ -1335,7 +1394,7 @@
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
- final TaskRecord task = reusedActivity.getTask();
+ final TaskRecord task = reusedActivity.getTaskRecord();
// In this situation we want to remove all activities from the task up to the one
// being started. In most cases this means we are resetting the task to its initial
@@ -1347,7 +1406,7 @@
// the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
// task reference is needed in the call below to
// {@link setTargetStackAndMoveToFrontIfNeeded}.
- if (reusedActivity.getTask() == null) {
+ if (reusedActivity.getTaskRecord() == null) {
reusedActivity.setTask(task);
}
@@ -1355,13 +1414,14 @@
if (top.frontOfTask) {
// Activity aliases may mean we use different intents for the top activity,
// so make sure the task now has the identity of the new intent.
- top.getTask().setIntent(mStartActivity);
+ top.getTaskRecord().setIntent(mStartActivity);
}
deliverNewIntent(top);
}
}
- mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
+ (false /* forceSend */, reusedActivity);
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
@@ -1401,7 +1461,7 @@
if (mStartActivity.packageName == null) {
final ActivityStack sourceStack = mStartActivity.resultTo != null
- ? mStartActivity.resultTo.getStack() : null;
+ ? mStartActivity.resultTo.getActivityStack() : null;
if (sourceStack != null) {
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
@@ -1413,12 +1473,12 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
- final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
final ActivityRecord topFocused = topStack.getTopActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
- && top.realActivity.equals(mStartActivity.realActivity)
- && top.userId == mStartActivity.userId
+ && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
+ && top.mUserId == mStartActivity.mUserId
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
@@ -1430,7 +1490,7 @@
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
ActivityOptions.abort(mOptions);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1443,7 +1503,7 @@
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
- mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
+ mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode,
mPreferredDisplayId, topStack);
return START_DELIVERED_TO_TOP;
@@ -1451,7 +1511,7 @@
boolean newTask = false;
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
- ? mSourceRecord.getTask() : null;
+ ? mSourceRecord.getTaskRecord() : null;
// Should this be considered a new task?
int result = START_SUCCESS;
@@ -1473,25 +1533,26 @@
}
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
- mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
+ mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
mService.getPackageManagerInternalLocked().grantEphemeralAccess(
- mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
+ mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
UserHandle.getAppId(mCallingUid));
if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
- mStartActivity.getTask().taskId);
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
+ mStartActivity.getTaskRecord().taskId);
}
ActivityStack.logStartActivity(
- EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
+ EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
mTargetStack.mLastPausedActivity = null;
- mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ false /* forceSend */, mStartActivity);
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
- mStartActivity.getTask().topRunningActivityLocked();
+ mStartActivity.getTaskRecord().topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
@@ -1512,19 +1573,19 @@
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
if (mTargetStack.isFocusable()
- && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
+ && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
- mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
- mOptions);
+ mRootActivityContainer.resumeFocusedStacksTopActivities(
+ mTargetStack, mStartActivity, mOptions);
}
} else if (mStartActivity != null) {
- mSupervisor.mRecentTasks.add(mStartActivity.getTask());
+ mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
}
- mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+ mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
- mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
- mPreferredDisplayId, mTargetStack);
+ mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
+ preferredWindowingMode, mPreferredDisplayId, mTargetStack);
return START_SUCCESS;
}
@@ -1642,7 +1703,7 @@
if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
r.mTaskOverlay = true;
if (!mOptions.canTaskOverlayResume()) {
- final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(
mOptions.getLaunchTaskId());
final ActivityRecord top = task != null ? task.getTopActivity() : null;
if (top != null && !top.isState(RESUMED)) {
@@ -1678,10 +1739,10 @@
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+ checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack()
.topRunningNonDelayedActivityLocked(mNotTop);
}
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ if (!checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
// Caller is not the same as launcher, so always needed.
mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
}
@@ -1692,7 +1753,7 @@
private void sendNewTaskResultRequestIfNeeded() {
final ActivityStack sourceStack = mStartActivity.resultTo != null
- ? mStartActivity.resultTo.getStack() : null;
+ ? mStartActivity.resultTo.getActivityStack() : null;
if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new task...
// yet the caller has requested a result back. Well, that is pretty messed up,
@@ -1796,7 +1857,7 @@
return;
}
if (!mSourceRecord.finishing) {
- mSourceStack = mSourceRecord.getStack();
+ mSourceStack = mSourceRecord.getActivityStack();
return;
}
@@ -1814,7 +1875,7 @@
// example, if this method is being called for processing a pending activity launch, it
// is possible that the activity has been removed from the task after the launch was
// enqueued.
- final TaskRecord sourceTask = mSourceRecord.getTask();
+ final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
}
mSourceRecord = null;
@@ -1840,26 +1901,28 @@
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
- final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId());
intentActivity = task != null ? task.getTopActivity() : null;
} else if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
- intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+ intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// For the launch adjacent case we only want to put the activity in an existing
// task if the activity already exists in the history.
- intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+ intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
!(LAUNCH_SINGLE_TASK == mLaunchMode));
} else {
// Otherwise find the best task to put the activity in.
- intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
+ intentActivity =
+ mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId);
}
}
- if (mStartActivity.isActivityTypeHome() && intentActivity != null
+ if (intentActivity != null
+ && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
&& intentActivity.getDisplayId() != mPreferredDisplayId) {
// Do not reuse home activity on other displays.
intentActivity = null;
@@ -1875,7 +1938,7 @@
* @return {@link ActivityRecord} brought to front.
*/
private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
- mTargetStack = intentActivity.getStack();
+ mTargetStack = intentActivity.getActivityStack();
mTargetStack.mLastPausedActivity = null;
// If the target task is not in the front, then we need to bring it to the front...
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
@@ -1886,9 +1949,9 @@
final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
- final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+ final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null;
differentTopTask = topTask != null
- && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+ && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask());
} else {
// The existing task should always be different from those in other displays.
differentTopTask = true;
@@ -1897,10 +1960,11 @@
if (differentTopTask && !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
- mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
+ mSourceStack.getTopActivity().getTaskRecord()
+ == mSourceRecord.getTaskRecord())) {
// We really do want to push this one into the user's face, right now.
if (mLaunchTaskBehind && mSourceRecord != null) {
- intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
+ intentActivity.setTaskToAffiliateWith(mSourceRecord.getTaskRecord());
}
// If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
@@ -1914,8 +1978,8 @@
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
if (!willClearTask) {
final ActivityStack launchStack = getLaunchStack(
- mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
- final TaskRecord intentTask = intentActivity.getTask();
+ mStartActivity, mLaunchFlags, mStartActivity.getTaskRecord(), mOptions);
+ final TaskRecord intentTask = intentActivity.getTaskRecord();
if (launchStack == null || launchStack == mTargetStack) {
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
@@ -1945,7 +2009,7 @@
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
+ intentActivity.getTaskRecord().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
@@ -1955,7 +2019,7 @@
// For example, the activity may have been initially started with an intent
// which placed it in the fullscreen stack. To ensure the proper handling of
// the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
+ intentActivity.getTaskRecord().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
@@ -1971,14 +2035,14 @@
}
// Need to update mTargetStack because if task was moved out of it, the original stack may
// be destroyed.
- mTargetStack = intentActivity.getStack();
+ mTargetStack = intentActivity.getActivityStack();
if (!mMovedToFront && mDoResume) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+ " from " + intentActivity);
mTargetStack.moveToFront("intentActivityFound");
}
- mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
+ mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTaskRecord(),
WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
// If the caller has requested that the target task be reset, then do so.
@@ -2000,14 +2064,14 @@
// launching another activity.
// TODO(b/36119896): We shouldn't trigger activity launches in this path since we are
// already launching one.
- final TaskRecord task = intentActivity.getTask();
+ final TaskRecord task = intentActivity.getTaskRecord();
task.performClearTaskLocked();
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
- ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
- mLaunchFlags);
+ final ActivityRecord top = intentActivity.getTaskRecord().performClearTaskLocked(
+ mStartActivity, mLaunchFlags);
if (top == null) {
// A special case: we need to start the activity because it is not currently
// running, and the caller has asked to clear the current task to have this
@@ -2019,7 +2083,7 @@
// Now pretend like this activity is being started by the top of its task, so it
// is put in the right place.
mSourceRecord = intentActivity;
- final TaskRecord task = mSourceRecord.getTask();
+ final TaskRecord task = mSourceRecord.getTaskRecord();
if (task != null && task.getStack() == null) {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
@@ -2029,19 +2093,21 @@
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
}
}
- } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
+ } else if (mStartActivity.mActivityComponent.equals(
+ intentActivity.getTaskRecord().realActivity)) {
// In this case the top activity on the task is the same as the one being launched,
// so we take that as a request to bring the task to the foreground. If the top
// activity in the task is the root activity, deliver this new intent to it if it
// desires.
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| LAUNCH_SINGLE_TOP == mLaunchMode)
- && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
+ && intentActivity.mActivityComponent.equals(
+ mStartActivity.mActivityComponent)) {
if (intentActivity.frontOfTask) {
- intentActivity.getTask().setIntent(mStartActivity);
+ intentActivity.getTaskRecord().setIntent(mStartActivity);
}
deliverNewIntent(intentActivity);
- } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
+ } else if (!intentActivity.getTaskRecord().isSameIntentFilter(mStartActivity)) {
// In this case we are launching the root activity of the task, but with a
// different intent. We should start a new instance on top.
mAddingToTask = true;
@@ -2054,23 +2120,23 @@
// current task.
mAddingToTask = true;
mSourceRecord = intentActivity;
- } else if (!intentActivity.getTask().rootWasReset) {
+ } else if (!intentActivity.getTaskRecord().rootWasReset) {
// In this case we are launching into an existing task that has not yet been started
// from its front door. The current task has been brought to the front. Ideally,
// we'd probably like to place this new task at the bottom of its stack, but that's
// a little hard to do with the current organization of the code so for now we'll
// just drop it.
- intentActivity.getTask().setIntent(mStartActivity);
+ intentActivity.getTaskRecord().setIntent(mStartActivity);
}
}
private void resumeTargetStackIfNeeded() {
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
+ mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions);
} else {
ActivityOptions.abort(mOptions);
}
- mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+ mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
}
private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
@@ -2081,16 +2147,16 @@
if (mReuseTask == null) {
final TaskRecord task = mTargetStack.createTaskRecord(
- mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
+ mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
+ updateBounds(mStartActivity.getTaskRecord(), mLaunchParams.mBounds);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in new task " + mStartActivity.getTask());
+ + " in new task " + mStartActivity.getTaskRecord());
} else {
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
@@ -2099,7 +2165,8 @@
mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
}
- if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ mStartActivity.getTaskRecord())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -2115,20 +2182,21 @@
return;
}
- ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+ ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
mIntentDelivered = true;
}
private int setTaskFromSourceRecord() {
- if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ mSourceRecord.getTaskRecord())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- final TaskRecord sourceTask = mSourceRecord.getTask();
- final ActivityStack sourceStack = mSourceRecord.getStack();
+ final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
+ final ActivityStack sourceStack = mSourceRecord.getActivityStack();
// We only want to allow changing stack in two cases:
// 1. If the target task is not the top one. Otherwise we would move the launching task to
// the other side, rather than show two side by side.
@@ -2138,19 +2206,19 @@
final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
|| !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
if (moveStackAllowed) {
- mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
- mOptions);
+ mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags,
+ mStartActivity.getTaskRecord(), mOptions);
// If target stack is not found now - we can't just rely on the source stack, as it may
// be not suitable. Let's check other displays.
if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
// Can't use target display, lets find a stack on the source display.
- mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
+ mTargetStack = mRootActivityContainer.getValidLaunchStackOnDisplay(
sourceStack.mDisplayId, mStartActivity, mOptions, mLaunchParams);
}
if (mTargetStack == null) {
// There are no suitable stacks on the target and source display(s). Look on all
// displays.
- mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
+ mTargetStack = mRootActivityContainer.getNextValidLaunchStack(
mStartActivity, -1 /* currentFocus */);
}
}
@@ -2176,12 +2244,12 @@
ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
mKeepCurTransition = true;
if (top != null) {
- ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
+ ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord());
deliverNewIntent(top);
// For paranoia, make sure we have correctly resumed the top activity.
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
ActivityOptions.abort(mOptions);
return START_DELIVERED_TO_TOP;
@@ -2192,14 +2260,14 @@
// stack if so.
final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
if (top != null) {
- final TaskRecord task = top.getTask();
+ final TaskRecord task = top.getTaskRecord();
task.moveActivityToFrontLocked(top);
top.updateOptionsLocked(mOptions);
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return START_DELIVERED_TO_TOP;
}
@@ -2209,7 +2277,8 @@
// the same task as the one that is starting it.
addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord);
+ + " in existing task " + mStartActivity.getTaskRecord()
+ + " from source " + mSourceRecord);
return START_SUCCESS;
}
@@ -2226,8 +2295,8 @@
// Check whether we should actually launch the new activity in to the task,
// or just reuse the current activity on top.
ActivityRecord top = mInTask.getTopActivity();
- if (top != null && top.realActivity.equals(mStartActivity.realActivity)
- && top.userId == mStartActivity.userId) {
+ if (top != null && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
+ && top.mUserId == mStartActivity.mUserId) {
if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) {
mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
@@ -2253,7 +2322,8 @@
if (!mLaunchParams.mBounds.isEmpty()) {
// TODO: Shouldn't we already know what stack to use by the time we get here?
- ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+ ActivityStack stack = mRootActivityContainer.getLaunchStack(
+ null, null, mInTask, ON_TOP);
if (stack != mInTask.getStack()) {
mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
DEFER_RESUME, "inTaskToFront");
@@ -2268,7 +2338,7 @@
addOrReparentStartingActivity(mInTask, "setTaskFromInTask");
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in explicit task " + mStartActivity.getTask());
+ + " in explicit task " + mStartActivity.getTaskRecord());
return START_SUCCESS;
}
@@ -2294,17 +2364,18 @@
mTargetStack.moveToFront("addingToTopTask");
}
final ActivityRecord prev = mTargetStack.getTopActivity();
- final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
- mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
+ final TaskRecord task = (prev != null)
+ ? prev.getTaskRecord() : mTargetStack.createTaskRecord(
+ mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
mTargetStack.positionChildWindowContainerAtTop(task);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in new guessed " + mStartActivity.getTask());
+ + " in new guessed " + mStartActivity.getTaskRecord());
}
private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
- if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
+ if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) {
parent.addActivityToTop(mStartActivity);
} else {
mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
@@ -2340,14 +2411,14 @@
private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
ActivityOptions aOptions) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
if (stack != null) {
return stack;
}
final ActivityStack currentStack = task != null ? task.getStack() : null;
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
if (currentStack != null) {
if (focusedStack != currentStack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -2368,18 +2439,18 @@
if (mPreferredDisplayId != DEFAULT_DISPLAY) {
// Try to put the activity in a stack on a secondary display.
- stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions,
- mLaunchParams);
+ stack = mRootActivityContainer.getValidLaunchStackOnDisplay(
+ mPreferredDisplayId, r, aOptions, mLaunchParams);
if (stack == null) {
// If source display is not suitable - look for topmost valid stack in the system.
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Can't launch on mPreferredDisplayId="
+ mPreferredDisplayId + ", looking on all displays.");
- stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
+ stack = mRootActivityContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
}
}
if (stack == null) {
- stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+ stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
@@ -2389,7 +2460,7 @@
/** Check if provided activity record can launch in currently focused stack. */
// TODO: This method can probably be consolidated into getLaunchStack() below.
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
final boolean canUseFocusedStack;
if (focusedStack.isActivityTypeAssistant()) {
canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2435,14 +2506,14 @@
// full resolution.
mLaunchParams.mPreferredDisplayId =
mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
- final ActivityStack stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
- mLaunchParams);
+ final ActivityStack stack =
+ mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
return stack;
}
// Otherwise handle adjacent launch.
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
// The parent activity doesn't want to launch the activity on top of itself, but
// instead tries to put it onto other side in side-by-side mode.
final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
@@ -2460,7 +2531,8 @@
if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
- final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+ final int activityType =
+ mRootActivityContainer.resolveActivityType(r, mOptions, task);
return parentStack.getDisplay().getOrCreateStack(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
} else {
@@ -2468,10 +2540,10 @@
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
final ActivityStack dockedStack =
- mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
- return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+ return mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
} else {
return dockedStack;
}
@@ -2659,7 +2731,7 @@
prefix = prefix + " ";
pw.print(prefix);
pw.print("mCurrentUser=");
- pw.println(mSupervisor.mCurrentUser);
+ pw.println(mRootActivityContainer.mCurrentUser);
pw.print(prefix);
pw.print("mLastStartReason=");
pw.println(mLastStartReason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d665592..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,
@@ -473,4 +474,6 @@
public abstract void setProfileApp(String profileApp);
public abstract void setProfileProc(WindowProcessController wpc);
public abstract void setProfilerInfo(ProfilerInfo profilerInfo);
+
+ public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4f01d699..61eb9d4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -91,8 +91,6 @@
.PACKAGE;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -122,6 +120,8 @@
import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -190,6 +190,7 @@
import android.os.IBinder;
import android.os.IUserManager;
import android.os.LocaleList;
+import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
@@ -246,7 +247,6 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
-import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -341,7 +341,8 @@
ActivityManagerInternal mAmInternal;
UriGrantsManagerInternal mUgmInternal;
private PackageManagerInternal mPmInternal;
- private ActivityTaskManagerInternal mInternal;
+ @VisibleForTesting
+ final ActivityTaskManagerInternal mInternal;
PowerManagerInternal mPowerManagerInternal;
private UsageStatsManagerInternal mUsageStatsInternal;
@@ -351,6 +352,7 @@
/* Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
ActivityStackSupervisor mStackSupervisor;
+ RootActivityContainer mRootActivityContainer;
WindowManagerService mWindowManager;
private UserManagerService mUserManager;
private AppOpsService mAppOpsService;
@@ -643,6 +645,7 @@
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
+ mInternal = new LocalService();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
}
@@ -750,9 +753,9 @@
return mGlobalLock;
}
- public void setActivityManagerService(IntentFirewall intentFirewall,
- PendingIntentController intentController) {
- mH = new H();
+ public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController,
+ Looper looper) {
+ mH = new H(looper);
mUiHandler = new UiHandler();
mIntentFirewall = intentFirewall;
final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -764,7 +767,8 @@
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
mStackSupervisor = createStackSupervisor();
- mStackSupervisor.onConfigurationChanged(mTempConfig);
+ mRootActivityContainer = new RootActivityContainer(this);
+ mRootActivityContainer.onConfigurationChanged(mTempConfig);
mTaskChangeNotificationController =
new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
@@ -799,6 +803,7 @@
mWindowManager = wm;
mLockTaskController.setWindowManager(wm);
mStackSupervisor.setWindowManager(wm);
+ mRootActivityContainer.setWindowManager(wm);
}
}
@@ -858,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();
}
@@ -893,7 +905,6 @@
}
private void start() {
- mInternal = new LocalService();
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@@ -1254,7 +1265,7 @@
sourceToken = resultTo;
}
- sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken);
+ sourceRecord = mRootActivityContainer.isInAnyStack(sourceToken);
if (sourceRecord == null) {
throw new SecurityException("Called with bad activity token: " + sourceToken);
}
@@ -1438,7 +1449,7 @@
return true;
}
// Keep track of the root activity of the task before we finish it
- TaskRecord tr = r.getTask();
+ final TaskRecord tr = r.getTaskRecord();
ActivityRecord rootR = tr.getRootActivity();
if (rootR == null) {
Slog.w(TAG, "Finishing task with all activities already finished");
@@ -1453,7 +1464,7 @@
// We should consolidate.
if (mController != null) {
// Find the first activity that is not finishing.
- ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
+ final ActivityRecord next = r.getActivityStack().topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
@@ -1515,7 +1526,7 @@
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
// can finish.
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}
@@ -1798,7 +1809,7 @@
}
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
mWindowManager.setAppFullscreen(token, true);
return translucentChanged;
@@ -1818,7 +1829,7 @@
if (r == null) {
return false;
}
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
int index = task.mActivities.lastIndexOf(r);
if (index > 0) {
ActivityRecord under = task.mActivities.get(index - 1);
@@ -1826,9 +1837,9 @@
}
final boolean translucentChanged = r.changeWindowTranslucency(false);
if (translucentChanged) {
- r.getStack().convertActivityToTranslucent(r);
+ r.getActivityStack().convertActivityToTranslucent(r);
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
@@ -1841,9 +1852,9 @@
public void notifyActivityDrawn(IBinder token) {
if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
synchronized (mGlobalLock) {
- ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+ ActivityRecord r = mRootActivityContainer.isInAnyStack(token);
if (r != null) {
- r.getStack().notifyActivityDrawnLocked(r);
+ r.getActivityStack().notifyActivityDrawnLocked(r);
}
}
}
@@ -1878,7 +1889,7 @@
synchronized (mGlobalLock) {
ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
- return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+ return mRootActivityContainer.getStackInfo(focusedStack.mStackId);
}
return null;
}
@@ -1894,14 +1905,14 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
return;
}
final ActivityRecord r = stack.topRunningActivityLocked();
if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
} finally {
@@ -1916,14 +1927,14 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
return;
}
final ActivityRecord r = task.topRunningActivityLocked();
if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
} finally {
@@ -1963,7 +1974,7 @@
synchronized (mGlobalLock) {
final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
if (srec != null) {
- return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+ return srec.getActivityStack().shouldUpRecreateTaskLocked(srec, destAffinity);
}
}
return false;
@@ -1976,7 +1987,8 @@
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
- return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
+ return r.getActivityStack().navigateUpToLocked(
+ r, destIntent, resultCode, resultData);
}
return false;
}
@@ -2008,7 +2020,7 @@
final long origId = Binder.clearCallingIdentity();
try {
int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task != null) {
return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
}
@@ -2026,7 +2038,7 @@
Rect rect = new Rect();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
@@ -2057,7 +2069,7 @@
synchronized (mGlobalLock) {
enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
- final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+ final TaskRecord tr = mRootActivityContainer.anyTaskForId(id,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
return tr.lastTaskDescription;
@@ -2077,7 +2089,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
@@ -2166,7 +2178,7 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task == null) {
Slog.d(TAG, "Could not find task for id: "+ taskId);
SafeActivityOptions.abort(options);
@@ -2283,7 +2295,7 @@
final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
callingUid);
- mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+ mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, callingUid, allowed);
}
@@ -2296,7 +2308,7 @@
final long origId = Binder.clearCallingIdentity();
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
+ r.getActivityStack().finishSubActivityLocked(r, resultWho, requestCode);
}
Binder.restoreCallingIdentity(origId);
}
@@ -2319,7 +2331,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
return;
@@ -2328,7 +2340,7 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
throw new IllegalStateException(
"moveTaskToStack: No stack for stackId=" + stackId);
@@ -2358,7 +2370,7 @@
try {
synchronized (mGlobalLock) {
if (animate) {
- final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+ final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
@@ -2370,12 +2382,12 @@
stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
animationDuration, false /* fromFullscreen */);
} else {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
}
- mStackSupervisor.resizeStackLocked(stack, destBounds,
+ mRootActivityContainer.resizeStack(stack, destBounds,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
}
@@ -2409,7 +2421,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
@@ -2451,7 +2463,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+ mRootActivityContainer.removeStacksInWindowingModes(windowingModes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2466,7 +2478,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+ mRootActivityContainer.removeStacksWithActivityTypes(activityTypes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2497,7 +2509,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mStackSupervisor.getAllStackInfosLocked();
+ return mRootActivityContainer.getAllStackInfos();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2510,7 +2522,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mStackSupervisor.getStackInfo(windowingMode, activityType);
+ return mRootActivityContainer.getStackInfo(windowingMode, activityType);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2541,7 +2553,7 @@
if (r == null) {
return;
}
- startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
+ startLockTaskModeLocked(r.getTaskRecord(), false /* isSystemCaller */);
}
}
@@ -2552,7 +2564,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
return;
@@ -2574,7 +2586,7 @@
if (r == null) {
return;
}
- stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+ stopLockTaskModeInternal(r.getTaskRecord(), false /* isSystemCaller */);
}
}
@@ -2594,7 +2606,7 @@
return;
}
- final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
if (stack == null || task != stack.topTask()) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
@@ -2609,7 +2621,7 @@
long ident = Binder.clearCallingIdentity();
try {
// When a task is locked, dismiss the pinned stack if it exists
- mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
@@ -2667,7 +2679,7 @@
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
r.setTaskDescription(td);
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
task.updateTaskDescription();
mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
}
@@ -2711,7 +2723,7 @@
try {
// TODO: VI Consider treating local voice interactions and voice tasks
// differently here
- mStackSupervisor.finishVoiceTask(session);
+ mRootActivityContainer.finishVoiceTask(session);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2723,7 +2735,7 @@
public boolean isTopOfTask(IBinder token) {
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
- return r != null && r.getTask().getTopActivity() == r;
+ return r != null && r.getTaskRecord().getTopActivity() == r;
}
}
@@ -2762,8 +2774,8 @@
}
if (structure != null) {
// Pre-fill the task/activity component for all assist data receivers
- structure.setTaskId(pae.activity.getTask().taskId);
- structure.setActivityComponent(pae.activity.realActivity);
+ structure.setTaskId(pae.activity.getTaskRecord().taskId);
+ structure.setActivityComponent(pae.activity.mActivityComponent);
structure.setHomeActivity(pae.isHome);
}
pae.haveResult = true;
@@ -2871,9 +2883,9 @@
+ ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
}
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
final TaskRecord task = stack.createTaskRecord(
- mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+ mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), ainfo, intent,
null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
@@ -2901,7 +2913,7 @@
@Override
public void setTaskResizeable(int taskId, int resizeableMode) {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(
taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
@@ -2917,7 +2929,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
@@ -2969,7 +2981,7 @@
if (r == null) {
return false;
}
- return r.getStack().safelyDestroyActivityLocked(r, "app-req");
+ return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req");
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2982,7 +2994,7 @@
final long origId = Binder.clearCallingIdentity();
try {
final WindowProcessController app = getProcessController(appInt);
- mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+ mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3076,7 +3088,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "removeStack: No stack with id=" + stackId);
return;
@@ -3101,7 +3113,7 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
+ " to displayId=" + displayId);
- mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+ mRootActivityContainer.moveStackToDisplay(stackId, displayId, ON_TOP);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3119,7 +3131,7 @@
"exitFreeformMode: No activity record matching token=" + token);
}
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
if (stack == null || !stack.inFreeformWindowingMode()) {
throw new IllegalStateException(
"exitFreeformMode: You can only go fullscreen from freeform.");
@@ -3405,7 +3417,7 @@
if (activity == null) {
return false;
}
- userId = activity.userId;
+ userId = activity.mUserId;
}
return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
}
@@ -3563,13 +3575,13 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
+ taskId + " in stackId=" + stackId + " at position=" + position);
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task == null) {
throw new IllegalArgumentException("positionTaskInStack: no task for id="
+ taskId);
}
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("positionTaskInStack: no stack for id="
@@ -3624,7 +3636,7 @@
try {
synchronized (mGlobalLock) {
final ActivityStack stack =
- mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
return;
@@ -3634,7 +3646,7 @@
// Caller wants the current split-screen primary stack to be the top stack after
// it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
- } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
+ } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
// In this case the current split-screen primary stack shouldn't be the top
// stack after it goes fullscreen, but it current has focus, so we move the
// focus to the top-most split-screen secondary stack next to it.
@@ -3665,7 +3677,7 @@
try {
synchronized (mGlobalLock) {
final PinnedActivityStack stack =
- mStackSupervisor.getDefaultDisplay().getPinnedStack();
+ mRootActivityContainer.getDefaultDisplay().getPinnedStack();
if (stack == null) {
Slog.w(TAG, "dismissPip: pinned stack not found.");
return;
@@ -3707,7 +3719,7 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(fromStackId);
if (stack != null){
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException(
@@ -3742,7 +3754,7 @@
long ident = Binder.clearCallingIdentity();
try {
- return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+ return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3779,14 +3791,14 @@
}
private boolean isInPictureInPictureMode(ActivityRecord r) {
- if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
- || r.getStack().isInStackLocked(r) == null) {
+ if (r == null || r.getActivityStack() == null || !r.inPinnedWindowingMode()
+ || r.getActivityStack().isInStackLocked(r) == null) {
return false;
}
// If we are animating to fullscreen then we have already dispatched the PIP mode
// changed, so we should reflect that check here as well.
- final PinnedActivityStack stack = r.getStack();
+ final PinnedActivityStack stack = r.getActivityStack();
final PinnedStackWindowController windowController = stack.getWindowContainerController();
return !windowController.isAnimatingBoundsToFullscreen();
}
@@ -3820,9 +3832,9 @@
// Adjust the source bounds by the insets for the transition down
final Rect sourceBounds = new Rect(
r.pictureInPictureArgs.getSourceRectHint());
- mStackSupervisor.moveActivityToPinnedStackLocked(
+ mRootActivityContainer.moveActivityToPinnedStack(
r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
- final PinnedActivityStack stack = r.getStack();
+ final PinnedActivityStack stack = r.getActivityStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
@@ -3866,7 +3878,7 @@
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
- final PinnedActivityStack stack = r.getStack();
+ final PinnedActivityStack stack = r.getActivityStack();
if (!stack.isAnimatingBoundsToFullscreen()) {
stack.setPictureInPictureAspectRatio(
r.pictureInPictureArgs.getAspectRatio());
@@ -3924,8 +3936,8 @@
}
if (params.hasSetAspectRatio()
- && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
- params.getAspectRatio())) {
+ && !mWindowManager.isValidPictureInPictureAspectRatio(
+ r.getActivityStack().mDisplayId, params.getAspectRatio())) {
final float minAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
final float maxAspectRatio = mContext.getResources().getFloat(
@@ -4011,7 +4023,7 @@
}
int err;
- if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+ if ((err = vrService.hasVrPackage(packageName, r.mUserId)) !=
VrManagerInternal.NO_ERROR) {
return err;
}
@@ -4041,7 +4053,7 @@
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
- if (mRunningVoice != null || activity.getTask().voiceSession != null
+ if (mRunningVoice != null || activity.getTaskRecord().voiceSession != null
|| activity.voiceSession != null) {
Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
return;
@@ -4099,7 +4111,7 @@
synchronized (mGlobalLock) {
// Check if display is initialized in AM.
- if (!mStackSupervisor.isDisplayAdded(displayId)) {
+ if (!mRootActivityContainer.isDisplayAdded(displayId)) {
// Call might come when display is not yet added or has already been removed.
if (DEBUG_CONFIGURATION) {
Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
@@ -4189,7 +4201,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
@@ -4209,7 +4221,7 @@
try {
final TaskRecord task;
synchronized (mGlobalLock) {
- task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
@@ -4251,7 +4263,7 @@
if (mLastResumedActivity == null) {
return getCurrentUserId();
}
- return mLastResumedActivity.userId;
+ return mLastResumedActivity.mUserId;
}
}
@@ -4429,9 +4441,9 @@
if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+ " to main stack for VR");
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
- moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+ moveTaskToStack(r.getTaskRecord().taskId, stack.mStackId, true /* toTop */);
}
mH.post(() -> {
if (!mVrController.onVrModeChanged(r)) {
@@ -4443,7 +4455,7 @@
if (disableNonVrUi) {
// If we are in a VR mode where Picture-in-Picture mode is unsupported,
// then remove the pinned stack.
- mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
}
}
});
@@ -4495,7 +4507,7 @@
}
ActivityStack getTopDisplayFocusedStack() {
- return mStackSupervisor.getTopDisplayFocusedStack();
+ return mRootActivityContainer.getTopDisplayFocusedStack();
}
/** Pokes the task persister. */
@@ -4507,6 +4519,21 @@
return mKeyguardController.isKeyguardLocked();
}
+ /**
+ * Clears launch params for the given package.
+ * @param packageNames the names of the packages of which the launch params are to be cleared
+ */
+ @Override
+ public void clearLaunchParamsForPackages(List<String> packageNames) {
+ mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "clearLaunchParamsForPackages");
+ synchronized (mGlobalLock) {
+ for (int i = 0; i < packageNames.size(); ++i) {
+ mStackSupervisor.mLaunchParamsPersister.removeRecordForPackage(packageNames.get(i));
+ }
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
@@ -4556,12 +4583,12 @@
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
pw.println(header);
- boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+ boolean printedAnything = mRootActivityContainer.dumpActivities(fd, pw, dumpAll, dumpClient,
dumpPackage);
boolean needSep = printedAnything;
boolean printed = ActivityStackSupervisor.printThisActivity(pw,
- mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep,
+ mRootActivityContainer.getTopResumedActivity(), dumpPackage, needSep,
" ResumedActivity: ");
if (printed) {
printedAnything = true;
@@ -4583,7 +4610,7 @@
void dumpActivityContainersLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
- mStackSupervisor.dumpChildrenNames(pw, " ");
+ mRootActivityContainer.dumpChildrenNames(pw, " ");
pw.println(" ");
}
@@ -4607,7 +4634,7 @@
ArrayList<ActivityRecord> activities;
synchronized (mGlobalLock) {
- activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+ activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly,
dumpFocusedStackOnly);
}
@@ -4627,7 +4654,7 @@
}
needSep = true;
synchronized (mGlobalLock) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (lastTask != task) {
lastTask = task;
pw.print("TASK "); pw.print(lastTask.affinity);
@@ -4681,26 +4708,21 @@
}
}
- void writeSleepStateToProto(ProtoOutputStream proto) {
- for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+ 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() {
@@ -4727,7 +4749,7 @@
* also corresponds to the merged configuration of the default display.
*/
Configuration getGlobalConfiguration() {
- return mStackSupervisor.getConfiguration();
+ return mRootActivityContainer.getConfiguration();
}
boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
@@ -4859,7 +4881,7 @@
mTempConfig.seq = increaseConfigurationSeqLocked();
// Update stored global config and notify everyone about the change.
- mStackSupervisor.onConfigurationChanged(mTempConfig);
+ mRootActivityContainer.onConfigurationChanged(mTempConfig);
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
@@ -4906,7 +4928,7 @@
// Override configuration of the default display duplicates global config, so we need to
// update it also. This will also notify WindowManager about changes.
- performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+ performDisplayOverrideConfigUpdate(mRootActivityContainer.getConfiguration(), deferResume,
DEFAULT_DISPLAY);
return changes;
@@ -4960,12 +4982,12 @@
private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
int displayId) {
- mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ mTempConfig.setTo(mRootActivityContainer.getDisplayOverrideConfiguration(displayId));
final int changes = mTempConfig.updateFrom(values);
if (changes != 0) {
Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+ mTempConfig + " for displayId=" + displayId);
- mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+ mRootActivityContainer.setDisplayOverrideConfiguration(mTempConfig, displayId);
final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
if (isDensityChange && displayId == DEFAULT_DISPLAY) {
@@ -5015,6 +5037,10 @@
return mAmInternal.isActivityStartsLoggingEnabled();
}
+ boolean isBackgroundActivityStartsEnabled() {
+ return mAmInternal.isBackgroundActivityStartsEnabled();
+ }
+
void enableScreenAfterBoot(boolean booted) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
@@ -5087,7 +5113,7 @@
/** Update AMS states when an activity is resumed. */
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task.isActivityTypeStandard()) {
if (mCurAppTimeTracker != r.appTimeTracker) {
// We are switching app tracking. Complete the current one.
@@ -5095,7 +5121,7 @@
mCurAppTimeTracker.stop();
mH.obtainMessage(
REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
- mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+ mRootActivityContainer.clearOtherAppTimeTrackers(r.appTimeTracker);
mCurAppTimeTracker = null;
}
if (r.appTimeTracker != null) {
@@ -5119,7 +5145,7 @@
if (mLastResumedActivity != null) {
final IVoiceInteractionSession session;
- final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
+ final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTaskRecord();
if (lastResumedActivityTask != null
&& lastResumedActivityTask.voiceSession != null) {
session = lastResumedActivityTask.voiceSession;
@@ -5137,8 +5163,8 @@
}
}
- if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
- mAmInternal.sendForegroundProfileChanged(r.userId);
+ if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) {
+ mAmInternal.sendForegroundProfileChanged(r.mUserId);
}
updateResumedAppTrace(r);
mLastResumedActivity = r;
@@ -5149,21 +5175,22 @@
applyUpdateVrModeLocked(r);
EventLogTags.writeAmSetResumedActivity(
- r == null ? -1 : r.userId,
+ r == null ? -1 : r.mUserId,
r == null ? "NULL" : r.shortComponentName,
reason);
}
ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
synchronized (mGlobalLock) {
- final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+ final ActivityTaskManagerInternal.SleepToken token =
+ mRootActivityContainer.createSleepToken(tag, displayId);
updateSleepIfNeededLocked();
return token;
}
}
void updateSleepIfNeededLocked() {
- final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+ final boolean shouldSleep = !mRootActivityContainer.hasAwakeDisplay();
final boolean wasSleeping = mSleeping;
boolean updateOomAdj = false;
@@ -5179,7 +5206,7 @@
mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
- mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+ mRootActivityContainer.applySleepTokens(true /* applyToStacks */);
if (wasSleeping) {
updateOomAdj = true;
}
@@ -5210,7 +5237,8 @@
void updateUsageStats(ActivityRecord component, boolean resumed) {
final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
- mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
+ mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId,
+ resumed);
mH.sendMessage(m);
}
@@ -5245,7 +5273,7 @@
mHeavyWeightProcess = root.app;
final Message m = PooledLambda.obtainMessage(
ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
- root.app, root.intent, root.userId);
+ root.app, root.intent, root.mUserId);
mH.sendMessage(m);
}
@@ -5355,7 +5383,7 @@
// TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
private void startTimeTrackingFocusedActivityLocked() {
- final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+ final ActivityRecord resumedActivity = mRootActivityContainer.getTopResumedActivity();
if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
mCurAppTimeTracker.start(resumedActivity.packageName);
}
@@ -5380,7 +5408,7 @@
/** Applies latest configuration and/or visibility updates if needed. */
private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
- final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack mainStack = mRootActivityContainer.getTopDisplayFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
@@ -5395,7 +5423,7 @@
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+ mRootActivityContainer.ensureActivitiesVisible(starting, changes,
!PRESERVE_WINDOWS);
}
}
@@ -5567,8 +5595,8 @@
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_SUPERVISOR_STACK_MSG = 200;
- public H() {
- super(DisplayThread.get().getLooper());
+ H(Looper looper) {
+ super(looper);
}
@Override
@@ -5611,9 +5639,9 @@
@Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
- ActivityRecord homeActivity =
- mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
- return homeActivity == null ? null : homeActivity.realActivity;
+ final ActivityRecord homeActivity =
+ mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId);
+ return homeActivity == null ? null : homeActivity.mActivityComponent;
}
}
@@ -5650,14 +5678,14 @@
@Override
public List<IBinder> getTopVisibleActivities() {
synchronized (mGlobalLock) {
- return mStackSupervisor.getTopVisibleActivities();
+ return mRootActivityContainer.getTopVisibleActivities();
}
}
@Override
public void notifyDockedStackMinimizedChanged(boolean minimized) {
synchronized (mGlobalLock) {
- mStackSupervisor.setDockedStackMinimized(minimized);
+ mRootActivityContainer.setDockedStackMinimized(minimized);
}
}
@@ -5738,7 +5766,7 @@
// We might change the visibilities here, so prepare an empty app transition which
// might be overridden later if we actually change visibilities.
final ActivityDisplay activityDisplay =
- mStackSupervisor.getActivityDisplay(displayId);
+ mRootActivityContainer.getActivityDisplay(displayId);
if (activityDisplay == null) {
return;
}
@@ -5747,7 +5775,7 @@
if (!wasTransitionSet) {
dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ 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.
@@ -5764,7 +5792,7 @@
public void notifyKeyguardTrustedChanged() {
synchronized (mGlobalLock) {
if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
}
}
@@ -5791,7 +5819,7 @@
"setFocusedActivity: No activity record matching token=" + token);
}
if (r.moveFocusableActivityToTop("setFocusedActivity")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -5942,7 +5970,7 @@
public boolean shuttingDown(boolean booted, int timeout) {
synchronized (mGlobalLock) {
mShuttingDown = true;
- mStackSupervisor.prepareForShutdownLocked();
+ mRootActivityContainer.prepareForShutdown();
updateEventDispatchingLocked(booted);
notifyTaskPersisterLocked(null, true);
return mStackSupervisor.shutdownLocked(timeout);
@@ -6049,7 +6077,7 @@
@Override
public void onPackageReplaced(ApplicationInfo aInfo) {
synchronized (mGlobalLock) {
- mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ mRootActivityContainer.updateActivityApplicationInfo(aInfo);
}
}
@@ -6079,7 +6107,7 @@
mH.post(() -> {
synchronized (mGlobalLock) {
final ActivityDisplay activityDisplay =
- mStackSupervisor.getActivityDisplay(displayId);
+ mRootActivityContainer.getActivityDisplay(displayId);
if (activityDisplay == null) {
// Call might come when display is not yet added or has been removed.
if (DEBUG_CONFIGURATION) {
@@ -6107,9 +6135,9 @@
int requestCode, int resultCode, Intent data) {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
- if (r != null && r.getStack() != null) {
- r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode,
- resultCode, data);
+ if (r != null && r.getActivityStack() != null) {
+ r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
+ requestCode, resultCode, data);
}
}
}
@@ -6162,14 +6190,14 @@
@Override
public boolean startHomeActivity(int userId, String reason) {
synchronized (mGlobalLock) {
- return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
+ return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
}
}
@Override
public boolean startHomeOnAllDisplays(int userId, String reason) {
synchronized (mGlobalLock) {
- return mStackSupervisor.startHomeOnAllDisplays(userId, reason);
+ return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);
}
}
@@ -6233,7 +6261,7 @@
Runnable finishInstrumentationCallback) {
synchronized (mGlobalLock) {
// Remove this application's activities from active lists.
- boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+ boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);
wpc.clearRecentTasks();
wpc.clearActivities();
@@ -6245,12 +6273,12 @@
mWindowManager.deferSurfaceLayout();
try {
if (!restarting && hasVisibleActivities
- && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+ && !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.
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
} finally {
mWindowManager.continueSurfaceLayout();
@@ -6279,7 +6307,7 @@
}
mWindowManager.closeSystemDialogs(reason);
- mStackSupervisor.closeSystemDialogsLocked();
+ mRootActivityContainer.closeSystemDialogs();
}
// Call into AM outside the synchronized block.
mAmInternal.broadcastCloseSystemDialogs(reason);
@@ -6293,9 +6321,9 @@
String packageName, Set<String> disabledClasses, int userId, boolean booted) {
synchronized (mGlobalLock) {
// Clean-up disabled activities.
- if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+ if (mRootActivityContainer.finishDisabledPackageActivities(
packageName, disabledClasses, true, false, userId) && booted) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
mStackSupervisor.scheduleIdleLocked();
}
@@ -6312,7 +6340,7 @@
boolean didSomething =
getActivityStartController().clearPendingActivityLaunches(packageName);
- didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+ didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName,
null, doit, evenPersistent, userId);
return didSomething;
}
@@ -6321,7 +6349,7 @@
@Override
public void resumeTopActivities(boolean scheduleIdle) {
synchronized (mGlobalLock) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
if (scheduleIdle) {
mStackSupervisor.scheduleIdleLocked();
}
@@ -6338,7 +6366,7 @@
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLock) {
- return mStackSupervisor.attachApplicationLocked(wpc);
+ return mRootActivityContainer.attachApplication(wpc);
}
}
@@ -6360,7 +6388,7 @@
// Showing launcher to avoid user entering credential twice.
startHomeActivity(currentUserId, "notifyLockedProfile");
}
- mStackSupervisor.lockAllProfileTasks(userId);
+ mRootActivityContainer.lockAllProfileTasks(userId);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -6381,9 +6409,9 @@
ActivityOptions activityOptions = options != null
? new ActivityOptions(options) : ActivityOptions.makeBasic();
final ActivityRecord homeActivity =
- mStackSupervisor.getDefaultDisplayHomeActivity();
+ mRootActivityContainer.getDefaultDisplayHomeActivity();
if (homeActivity != null) {
- activityOptions.setLaunchTaskId(homeActivity.getTask().taskId);
+ activityOptions.setLaunchTaskId(homeActivity.getTaskRecord().taskId);
}
mContext.startActivityAsUser(intent, activityOptions.toBundle(),
UserHandle.CURRENT);
@@ -6398,7 +6426,7 @@
synchronized (mGlobalLock) {
// The output proto of "activity --proto activities"
// is ActivityManagerServiceDumpActivitiesProto
- mStackSupervisor.writeToProto(proto,
+ mRootActivityContainer.writeToProto(proto,
ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
}
}
@@ -6493,7 +6521,7 @@
}
if (dumpPackage == null) {
pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
- mStackSupervisor.dumpDisplayConfigs(pw, " ");
+ mRootActivityContainer.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
if (dumpPackage == null) {
@@ -6521,7 +6549,7 @@
if (dumpPackage == null) {
pw.println(" mWakefulness="
+ PowerManagerInternal.wakefulnessToString(wakefulness));
- pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
+ pw.println(" mSleepTokens=" + mRootActivityContainer.mSleepTokens);
if (mRunningVoice != null) {
pw.println(" mRunningVoice=" + mRunningVoice);
pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
@@ -6573,12 +6601,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());
@@ -6648,14 +6688,14 @@
@Override
public boolean canGcNow() {
synchronized (mGlobalLock) {
- return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+ return isSleeping() || mRootActivityContainer.allResumedActivitiesIdle();
}
}
@Override
public WindowProcessController getTopApp() {
synchronized (mGlobalLock) {
- final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+ final ActivityRecord top = mRootActivityContainer.getTopResumedActivity();
return top != null ? top.app : null;
}
}
@@ -6663,8 +6703,8 @@
@Override
public void rankTaskLayersIfNeeded() {
synchronized (mGlobalLock) {
- if (mStackSupervisor != null) {
- mStackSupervisor.rankTaskLayersIfNeeded();
+ if (mRootActivityContainer != null) {
+ mRootActivityContainer.rankTaskLayersIfNeeded();
}
}
}
@@ -6672,35 +6712,35 @@
@Override
public void scheduleDestroyAllActivities(String reason) {
synchronized (mGlobalLock) {
- mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+ mRootActivityContainer.scheduleDestroyAllActivities(null, reason);
}
}
@Override
public void removeUser(int userId) {
synchronized (mGlobalLock) {
- mStackSupervisor.removeUserLocked(userId);
+ mRootActivityContainer.removeUser(userId);
}
}
@Override
public boolean switchUser(int userId, UserState userState) {
synchronized (mGlobalLock) {
- return mStackSupervisor.switchUserLocked(userId, userState);
+ return mRootActivityContainer.switchUser(userId, userState);
}
}
@Override
public void onHandleAppCrash(WindowProcessController wpc) {
synchronized (mGlobalLock) {
- mStackSupervisor.handleAppCrashLocked(wpc);
+ mRootActivityContainer.handleAppCrash(wpc);
}
}
@Override
public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
synchronized (mGlobalLock) {
- return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+ return mRootActivityContainer.finishTopCrashedActivities(crashedApp, reason);
}
}
@@ -6870,5 +6910,12 @@
mProfilerInfo = profilerInfo;
}
}
+
+ @Override
+ public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.getActivityMetricsLogger().getLaunchObserverRegistry();
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 04fef02..441c593 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import android.app.ActivityManager;
import android.app.IAppTask;
@@ -77,7 +77,7 @@
synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -115,7 +115,7 @@
TaskRecord tr;
IApplicationThread appThread;
synchronized (mService.mGlobalLock) {
- tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -143,7 +143,7 @@
synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 32a6f74..bf00ffb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -305,7 +305,7 @@
AppWindowToken wtoken = openingApps.valueAt(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
- if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
+ if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
// This token isn't going to be animating. Add it to the list of tokens to
// be notified of app transition complete since the notification will not be
// sent be the app window animator.
@@ -341,7 +341,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
// TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
// animating?
- wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
+ wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
wtoken.updateReportedVisibilityLocked();
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
@@ -350,9 +350,8 @@
wtoken.deferClearAllDrawn = false;
// Ensure that apps that are mid-starting are also scheduled to have their
// starting windows removed after the animation is complete
- if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
- && wtoken.getController() != null) {
- wtoken.getController().removeStartingWindow();
+ if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
+ wtoken.removeStartingWindow();
}
if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 0436857..6c3fbc1 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -123,7 +123,8 @@
// TODO(b/75318890): Need to move this to when the app actually crashes.
if (/*ActivityManager.isRunningInTestHarness()
- &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
+ &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(
+ r.mActivityComponent)) {
// Don't show warning if we are running in a test harness and we don't have to always
// show for this activity.
return;
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
deleted file mode 100644
index bd1460a..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ /dev/null
@@ -1,914 +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 android.app.ActivityOptions.ANIM_CLIP_REVEAL;
-import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_NONE;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
-import static android.app.ActivityOptions.ANIM_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.AttributeCache;
-import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-
-/**
- * Controller for the app window token container. This is created by activity manager to link
- * activity records to the app window token container they use in window manager.
- *
- * Test class: {@link AppWindowContainerControllerTests}
- */
-public class AppWindowContainerController
- extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
-
- private static final int STARTING_WINDOW_TYPE_NONE = 0;
- private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
- private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
-
- private final IApplicationToken mToken;
- private final Handler mHandler;
-
- private final class H extends Handler {
- public static final int NOTIFY_WINDOWS_DRAWN = 1;
- public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
- public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
-
- public H(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case NOTIFY_WINDOWS_DRAWN:
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
- break;
- case NOTIFY_STARTING_WINDOW_DRAWN:
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onStartingWindowDrawn(msg.getWhen());
- break;
- case NOTIFY_WINDOWS_NOTDRAWN:
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
- break;
- default:
- break;
- }
- }
- }
-
- private final Runnable mOnWindowsVisible = () -> {
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsVisible();
- };
-
- private final Runnable mOnWindowsGone = () -> {
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsGone();
- };
-
- private final Runnable mAddStartingWindow = new Runnable() {
-
- @Override
- public void run() {
- final StartingData startingData;
- final AppWindowToken container;
-
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
- + " add starting window");
- return;
- }
-
- // There can only be one adding request, silly caller!
- mService.mAnimationHandler.removeCallbacks(this);
-
- startingData = mContainer.startingData;
- container = mContainer;
- }
-
- if (startingData == null) {
- // Animation has been canceled... do nothing.
- if (DEBUG_STARTING_WINDOW)
- Slog.v(TAG_WM, "startingData was nulled out before handling"
- + " mAddStartingWindow: " + mContainer);
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
- + AppWindowContainerController.this + ": startingData="
- + container.startingData);
-
- StartingSurface surface = null;
- try {
- surface = startingData.createStartingSurface(container);
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when adding starting window", e);
- }
- if (surface != null) {
- boolean abort = false;
- synchronized (mGlobalLock) {
- // If the window was successfully added, then
- // we need to remove it.
- if (container.removed || container.startingData == null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Aborted starting " + container
- + ": removed=" + container.removed
- + " startingData=" + container.startingData);
- container.startingWindow = null;
- container.startingData = null;
- abort = true;
- } else {
- container.startingSurface = surface;
- }
- if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
- "Added starting " + mContainer
- + ": startingWindow="
- + container.startingWindow + " startingView="
- + container.startingSurface);
- }
- if (abort) {
- surface.remove();
- }
- } else if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
- }
- }
- };
-
- public AppWindowContainerController(TaskWindowContainerController taskController,
- IApplicationToken token, ComponentName activityComponent,
- AppWindowContainerListener listener, int index, int requestedOrientation,
- boolean fullscreen, boolean showForAllUsers, int configChanges,
- boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
- int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
- this(taskController, token, activityComponent, listener, index, requestedOrientation,
- fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind,
- alwaysFocusable, targetSdkVersion, rotationAnimationHint,
- inputDispatchingTimeoutNanos, WindowManagerService.getInstance());
- }
-
- public AppWindowContainerController(TaskWindowContainerController taskController,
- IApplicationToken token, ComponentName activityComponent,
- AppWindowContainerListener listener, int index, int requestedOrientation,
- boolean fullscreen, boolean showForAllUsers, int configChanges,
- boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
- int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
- WindowManagerService service) {
- super(listener, service);
- mHandler = new H(service.mH.getLooper());
- mToken = token;
- synchronized (mGlobalLock) {
- AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
- if (atoken != null) {
- // TODO: Should this throw an exception instead?
- Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
- return;
- }
-
- final Task task = taskController.mContainer;
- if (task == null) {
- throw new IllegalArgumentException("AppWindowContainerController: invalid "
- + " controller=" + taskController);
- }
-
- atoken = createAppWindow(mService, token, activityComponent, voiceInteraction,
- task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen,
- showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint,
- configChanges, launchTaskBehind, alwaysFocusable, this);
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
- + " controller=" + taskController + " at " + index);
- task.addChild(atoken, index);
- }
- }
-
- @VisibleForTesting
- AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
- ComponentName component, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
- int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
- boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
- return new AppWindowToken(service, token, component, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
- rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
- controller);
- }
-
- public void removeContainer(int displayId) {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
- + mToken + " from non-existing displayId=" + displayId);
- return;
- }
- dc.removeAppToken(mToken.asBinder());
- super.removeContainer();
- }
- }
-
- @Override
- public void removeContainer() {
- throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
- }
-
- public void reparent(TaskWindowContainerController taskController, int position) {
- synchronized (mGlobalLock) {
- if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
- + " to task=" + taskController + " at " + position);
- if (mContainer == null) {
- if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
- "reparent: could not find app token=" + mToken);
- return;
- }
- final Task task = taskController.mContainer;
- if (task == null) {
- throw new IllegalArgumentException("reparent: could not find task="
- + taskController);
- }
- mContainer.reparent(task, position);
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
- }
-
- public Configuration setOrientation(int requestedOrientation, int displayId,
- Configuration displayConfig, boolean freezeScreenIfNeeded) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM,
- "Attempted to set orientation of non-existing app token: " + mToken);
- return null;
- }
-
- mContainer.setOrientation(requestedOrientation);
-
- final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
- return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
-
- }
- }
-
- public int getOrientation() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return SCREEN_ORIENTATION_UNSPECIFIED;
- }
-
- return mContainer.getOrientationIgnoreVisibility();
- }
- }
-
- public void setDisablePreviewScreenshots(boolean disable) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
- + " token: " + mToken);
- return;
- }
- mContainer.setDisablePreviewScreenshots(disable);
- }
- }
-
- public void setVisibility(boolean visible, boolean deferHidingClient) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
- + mToken);
- return;
- }
-
- final AppWindowToken wtoken = mContainer;
- final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition;
-
- // Don't set visibility to false if we were already not visible. This prevents WM from
- // adding the app to the closing app list which doesn't make sense for something that is
- // already not visible. However, set visibility to true even if we are already visible.
- // This makes sure the app is added to the opening apps list so that the right
- // transition can be selected.
- // TODO: Probably a good idea to separate the concept of opening/closing apps from the
- // concept of setting visibility...
- if (!visible && wtoken.hiddenRequested) {
-
- if (!deferHidingClient && wtoken.mDeferHidingClient) {
- // We previously deferred telling the client to hide itself when visibility was
- // initially set to false. Now we would like it to hide, so go ahead and set it.
- wtoken.mDeferHidingClient = deferHidingClient;
- wtoken.setClientHidden(true);
- }
- return;
- }
-
- if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
- + mToken + ", visible=" + visible + "): " + appTransition
- + " hidden=" + wtoken.isHidden() + " hiddenRequested="
- + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
-
- final DisplayContent displayContent = mContainer.getDisplayContent();
- displayContent.mOpeningApps.remove(wtoken);
- displayContent.mClosingApps.remove(wtoken);
- wtoken.waitingToShow = false;
- wtoken.hiddenRequested = !visible;
- wtoken.mDeferHidingClient = deferHidingClient;
-
- if (!visible) {
- // If the app is dead while it was visible, we kept its dead window on screen.
- // Now that the app is going invisible, we can remove it. It will be restarted
- // if made visible again.
- wtoken.removeDeadWindows();
- } else {
- if (!appTransition.isTransitionSet()
- && appTransition.isReady()) {
- // Add the app mOpeningApps if transition is unset but ready. This means
- // we're doing a screen freeze, and the unfreeze will wait for all opening
- // apps to be ready.
- displayContent.mOpeningApps.add(wtoken);
- }
- wtoken.startingMoved = false;
- // If the token is currently hidden (should be the common case), or has been
- // stopped, then we need to set up to wait for its windows to be ready.
- if (wtoken.isHidden() || wtoken.mAppStopped) {
- wtoken.clearAllDrawn();
-
- // If the app was already visible, don't reset the waitingToShow state.
- if (wtoken.isHidden()) {
- wtoken.waitingToShow = true;
- }
- }
-
- // In the case where we are making an app visible but holding off for a transition,
- // we still need to tell the client to make its windows visible so they get drawn.
- // Otherwise, we will wait on performing the transition until all windows have been
- // drawn, they never will be, and we are sad.
- wtoken.setClientHidden(false);
-
- wtoken.requestUpdateWallpaperIfNeeded();
-
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
- wtoken.mAppStopped = false;
-
- mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
- }
-
- // If we are preparing an app transition, then delay changing
- // the visibility of this token until we execute that transition.
- if (wtoken.okToAnimate() && appTransition.isTransitionSet()) {
- wtoken.inPendingTransaction = true;
- if (visible) {
- displayContent.mOpeningApps.add(wtoken);
- wtoken.mEnteringAnimation = true;
- } else {
- displayContent.mClosingApps.add(wtoken);
- wtoken.mEnteringAnimation = false;
- }
- if (appTransition.getAppTransition()
- == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
- // We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
- if (win != null) {
- final AppWindowToken focusedToken = win.mAppToken;
- if (focusedToken != null) {
- if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
- + " adding " + focusedToken + " to mOpeningApps");
- // Force animation to be loaded.
- focusedToken.setHidden(true);
- displayContent.mOpeningApps.add(focusedToken);
- }
- }
- }
- return;
- }
-
- wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
- wtoken.updateReportedVisibilityLocked();
- }
- }
-
- /**
- * Notifies that we launched an app that might be visible or not visible depending on what kind
- * of Keyguard flags it's going to set on its windows.
- */
- public void notifyUnknownVisibilityLaunched() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(
- mContainer);
- }
- }
- }
-
- public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
- CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
- IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
- synchronized (mGlobalLock) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
- + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
- + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
- + " allowTaskSnapshot=" + allowTaskSnapshot);
-
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
- return false;
- }
-
- // If the display is frozen, we won't do anything until the actual window is
- // displayed so there is no reason to put in the starting window.
- if (!mContainer.okToDisplay()) {
- return false;
- }
-
- if (mContainer.startingData != null) {
- return false;
- }
-
- final WindowState mainWin = mContainer.findMainWindow();
- if (mainWin != null && mainWin.mWinAnimator.getShown()) {
- // App already has a visible window...why would you want a starting window?
- return false;
- }
-
- final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
- mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
- false /* restoreFromDisk */, false /* reducedResolution */);
- final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, fromRecents, snapshot);
-
- if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
- return createSnapshot(snapshot);
- }
-
- // If this is a translucent window, then don't show a starting window -- the current
- // effect (a full-screen opaque starting window that fades away to the real contents
- // when it is ready) does not work for this.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
- + Integer.toHexString(theme));
- if (theme != 0) {
- AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
- com.android.internal.R.styleable.Window, mService.mCurrentUserId);
- if (ent == null) {
- // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
- // see that.
- return false;
- }
- final boolean windowIsTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowIsFloating = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false);
- final boolean windowShowWallpaper = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false);
- final boolean windowDisableStarting = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false);
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
- + " Floating=" + windowIsFloating
- + " ShowWallpaper=" + windowShowWallpaper);
- if (windowIsTranslucent) {
- return false;
- }
- if (windowIsFloating || windowDisableStarting) {
- return false;
- }
- if (windowShowWallpaper) {
- if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
- == null) {
- // If this theme is requesting a wallpaper, and the wallpaper
- // is not currently visible, then this effectively serves as
- // an opaque window and our starting window transition animation
- // can still work. We just need to make sure the starting window
- // is also showing the wallpaper.
- windowFlags |= FLAG_SHOW_WALLPAPER;
- } else {
- return false;
- }
- }
- }
-
- if (mContainer.transferStartingWindow(transferFrom)) {
- return true;
- }
-
- // There is no existing starting window, and we don't want to create a splash screen, so
- // that's it!
- if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
- mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
- compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- mContainer.getMergedOverrideConfiguration());
- scheduleAddStartingWindow();
- }
- return true;
- }
-
- private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
- TaskSnapshot snapshot) {
- if (mContainer.getDisplayContent().mAppTransition.getAppTransition()
- == TRANSIT_DOCK_TASK_FROM_RECENTS) {
- // TODO(b/34099271): Remove this statement to add back the starting window and figure
- // out why it causes flickering, the starting window appears over the thumbnail while
- // the docked from recents transition occurs
- return STARTING_WINDOW_TYPE_NONE;
- } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
- return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else if (taskSwitch && allowTaskSnapshot) {
- return snapshot == null ? STARTING_WINDOW_TYPE_NONE
- : snapshotOrientationSameAsTask(snapshot) || fromRecents
- ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else {
- return STARTING_WINDOW_TYPE_NONE;
- }
- }
-
- void scheduleAddStartingWindow() {
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
- mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
- }
- }
-
- private boolean createSnapshot(TaskSnapshot snapshot) {
- if (snapshot == null) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
- mContainer.startingData = new SnapshotStartingData(mService, snapshot);
- scheduleAddStartingWindow();
- return true;
- }
-
- private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
- if (snapshot == null) {
- return false;
- }
- return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
- }
-
- public void removeStartingWindow() {
- synchronized (mGlobalLock) {
- if (mContainer.startingWindow == null) {
- if (mContainer.startingData != null) {
- // Starting window has not been added yet, but it is scheduled to be added.
- // Go ahead and cancel the request.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Clearing startingData for token=" + mContainer);
- mContainer.startingData = null;
- }
- return;
- }
-
- final StartingSurface surface;
- if (mContainer.startingData != null) {
- surface = mContainer.startingSurface;
- mContainer.startingData = null;
- mContainer.startingSurface = null;
- mContainer.startingWindow = null;
- mContainer.startingDisplayed = false;
- if (surface == null) {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
- + "remove");
- }
- return;
- }
- } else {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
- + mContainer);
- }
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
- + " startingWindow=" + mContainer.startingWindow
- + " startingView=" + mContainer.startingSurface
- + " Callers=" + Debug.getCallers(5));
-
- // Use the same thread to remove the window as we used to add it, as otherwise we end up
- // with things in the view hierarchy being called from different threads.
- mService.mAnimationHandler.post(() -> {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
- try {
- surface.remove();
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- });
- }
- }
-
- public void pauseKeyDispatching() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
- }
- }
- }
-
- public void resumeKeyDispatching() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
- }
- }
- }
-
- public void notifyAppResumed(boolean wasStopped) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
- return;
- }
- mContainer.notifyAppResumed(wasStopped);
- }
- }
-
- public void notifyAppStopping() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
- + mToken);
- return;
- }
- mContainer.detachChildren();
- }
- }
-
- public void notifyAppStopped() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
- + mToken);
- return;
- }
- mContainer.notifyAppStopped();
- }
- }
-
- public void startFreezingScreen(int configChanges) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM,
- "Attempted to freeze screen with non-existing app token: " + mContainer);
- return;
- }
-
- if (configChanges == 0 && mContainer.okToDisplay()) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
- return;
- }
-
- mContainer.startFreezingScreen();
- }
- }
-
- public void stopFreezingScreen(boolean force) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return;
- }
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
- + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen());
- mContainer.stopFreezingScreen(true, force);
- }
- }
-
- public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
- + " token: " + mToken);
- return;
- }
- mContainer.registerRemoteAnimations(definition);
- }
- }
-
- void reportStartingWindowDrawn() {
- mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
- }
-
- void reportWindowsDrawn() {
- mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
- }
-
- void reportWindowsNotDrawn() {
- mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
- }
-
- void reportWindowsVisible() {
- mHandler.post(mOnWindowsVisible);
- }
-
- void reportWindowsGone() {
- mHandler.post(mOnWindowsGone);
- }
-
- /** Calls directly into activity manager so window manager lock shouldn't held. */
- boolean keyDispatchingTimedOut(String reason, int windowPid) {
- return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
- }
-
- /**
- * Apply override app transition base on options & animation type.
- */
- public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
- synchronized (mGlobalLock) {
- final int animationType = pendingOptions.getAnimationType();
- final DisplayContent displayContent = mContainer.getDisplayContent();
- switch (animationType) {
- case ANIM_CUSTOM:
- displayContent.mAppTransition.overridePendingAppTransition(
- pendingOptions.getPackageName(),
- pendingOptions.getCustomEnterResId(),
- pendingOptions.getCustomExitResId(),
- pendingOptions.getOnAnimationStartListener());
- break;
- case ANIM_CLIP_REVEAL:
- displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- break;
- case ANIM_SCALE_UP:
- displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- break;
- case ANIM_THUMBNAIL_SCALE_UP:
- case ANIM_THUMBNAIL_SCALE_DOWN:
- final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
- final GraphicBuffer buffer = pendingOptions.getThumbnail();
- displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getOnAnimationStartListener(),
- scaleUp);
- if (intent.getSourceBounds() == null && buffer != null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + buffer.getWidth(),
- pendingOptions.getStartY() + buffer.getHeight()));
- }
- break;
- case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
- case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
- final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
- 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);
- } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
- && specs != null) {
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
- specs, pendingOptions.getOnAnimationStartListener(),
- pendingOptions.getAnimationFinishedListener(), false);
- } else {
- displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
- pendingOptions.getThumbnail(),
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getOnAnimationStartListener(),
- (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- }
- break;
- case ANIM_OPEN_CROSS_PROFILE_APPS:
- displayContent.mAppTransition
- .overridePendingAppTransitionStartCrossProfileApps();
- break;
- case ANIM_REMOTE_ANIMATION:
- // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
- displayContent.mAppTransition.overridePendingAppTransitionRemote(
- pendingOptions.getRemoteAnimationAdapter());
- break;
- case ANIM_NONE:
- break;
- default:
- Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
- break;
- }
- }
- }
-
- /**
- * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
- * This information helps AWT know that the app is in the process of pausing before it gets the
- * signal on the WM side.
- */
- public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return;
- }
-
- mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip);
- }
- }
-
- @Override
- public String toString() {
- return "AppWindowContainerController{"
- + " token=" + mToken
- + " mContainer=" + mContainer
- + " mListener=" + mListener
- + "}";
- }
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
deleted file mode 100644
index ad27669..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ /dev/null
@@ -1,42 +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;
-
-/** Interface used by the creator of the controller to listen to changes with the container. */
-public interface AppWindowContainerListener extends WindowContainerListener {
- /** Called when the windows associated app window container drawn state changes. */
- void onWindowsDrawn(boolean drawn, long timestamp);
- /** Called when the windows associated app window container are visible. */
- void onWindowsVisible();
- /** Called when the windows associated app window container are no longer visible. */
- void onWindowsGone();
-
- /**
- * Called when the starting window for this container is drawn.
- */
- void onStartingWindowDrawn(long timestamp);
-
- /**
- * Called when the key dispatching to a window associated with the app window container
- * timed-out.
- *
- * @param reason The reason for the key dispatching time out.
- * @param windowPid The pid of the window key dispatching timed out on.
- * @return True if input dispatching should be aborted.
- */
- boolean keyDispatchingTimedOut(String reason, int windowPid);
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 729f89b..bb38f30 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -16,13 +16,13 @@
package com.android.server.wm;
+import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
+import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
-import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
-import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
@@ -53,7 +53,8 @@
AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
mAppToken = appToken;
- mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService);
+ mSurfaceAnimator =
+ new SurfaceAnimator(this, this::onAnimationFinished, appToken.mWmService);
mWidth = thumbnailHeader.getWidth();
mHeight = thumbnailHeader.getHeight();
@@ -65,7 +66,7 @@
// this to the task.
mSurfaceControl = appToken.makeSurface()
.setName("thumbnail anim: " + appToken.toString())
- .setSize(mWidth, mHeight)
+ .setBufferSize(mWidth, mHeight)
.setFormat(PixelFormat.TRANSLUCENT)
.setMetadata(appToken.windowType,
window != null ? window.mOwnerUid : Binder.getCallingUid())
@@ -93,11 +94,11 @@
void startAnimation(Transaction t, Animation anim, Point position) {
anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked());
+ anim.scaleCurrentDuration(mAppToken.mWmService.getTransitionAnimationScaleLocked());
mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position,
mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()),
- mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */);
+ mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
private void onAnimationFinished() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 92944a0..c458c94 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -28,10 +28,12 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
@@ -81,7 +83,9 @@
import android.annotation.CallSuper;
import android.app.Activity;
+import android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
@@ -90,6 +94,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -106,6 +111,8 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
+import com.android.server.AttributeCache;
+import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
import com.android.server.wm.WindowManagerService.H;
@@ -121,7 +128,8 @@
* Version of WindowToken that is specifically for a particular application (or
* really activity) that is displaying windows.
*/
-class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener,
+ ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
/**
@@ -226,6 +234,9 @@
private Task mLastParent;
+ // TODO: Remove after unification
+ ActivityRecord mActivityRecord;
+
/**
* See {@link #canTurnScreenOn()}
*/
@@ -273,14 +284,20 @@
/** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
boolean mNeedsAnimationBoundsLayer;
+ private static final int STARTING_WINDOW_TYPE_NONE = 0;
+ private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+ private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
AppWindowToken(WindowManagerService service, IApplicationToken token,
ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
+ ActivityRecord activityRecord) {
this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
- setController(controller);
+ // TODO: remove after unification
+ mActivityRecord = activityRecord;
+ mActivityRecord.registerConfigurationChangeListener(this);
mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
mShowForAllUsers = showForAllUsers;
mTargetSdk = targetSdk;
@@ -304,7 +321,7 @@
mActivityComponent = activityComponent;
mVoiceInteraction = voiceInteraction;
mFillsParent = fillsParent;
- mInputApplicationHandle = new InputApplicationHandle(this);
+ mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
}
void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
@@ -320,9 +337,7 @@
// it from behind the starting window, so there is no need for it to also be doing its
// own stuff.
win.cancelAnimation();
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
}
updateReportedVisibilityLocked();
}
@@ -360,16 +375,9 @@
}
if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
+ numInteresting + " visible=" + numVisible);
- final AppWindowContainerController controller = getController();
if (nowDrawn != reportedDrawn) {
- if (nowDrawn) {
- if (controller != null) {
- controller.reportWindowsDrawn();
- }
- } else {
- if (controller != null) {
- controller.reportWindowsNotDrawn();
- }
+ if (mActivityRecord != null) {
+ mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
}
reportedDrawn = nowDrawn;
}
@@ -377,16 +385,36 @@
if (DEBUG_VISIBILITY) Slog.v(TAG,
"Visibility changed in " + this + ": vis=" + nowVisible);
reportedVisible = nowVisible;
- if (controller != null) {
+ if (mActivityRecord != null) {
if (nowVisible) {
- controller.reportWindowsVisible();
+ onWindowsVisible();
} else {
- controller.reportWindowsGone();
+ onWindowsGone();
}
}
}
}
+ private void onWindowsGone() {
+ if (mActivityRecord == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) {
+ Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken);
+ }
+ mActivityRecord.onWindowsGone();
+ }
+
+ private void onWindowsVisible() {
+ if (mActivityRecord == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) {
+ Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken);
+ }
+ mActivityRecord.onWindowsVisible();
+ }
+
boolean isClientHidden() {
return mClientHidden;
}
@@ -401,7 +429,116 @@
sendAppVisibilityToClients();
}
- boolean setVisibility(WindowManager.LayoutParams lp,
+ void setVisibility(boolean visible, boolean deferHidingClient) {
+ final AppTransition appTransition = getDisplayContent().mAppTransition;
+
+ // Don't set visibility to false if we were already not visible. This prevents WM from
+ // adding the app to the closing app list which doesn't make sense for something that is
+ // already not visible. However, set visibility to true even if we are already visible.
+ // This makes sure the app is added to the opening apps list so that the right
+ // transition can be selected.
+ // TODO: Probably a good idea to separate the concept of opening/closing apps from the
+ // concept of setting visibility...
+ if (!visible && hiddenRequested) {
+
+ if (!deferHidingClient && mDeferHidingClient) {
+ // We previously deferred telling the client to hide itself when visibility was
+ // initially set to false. Now we would like it to hide, so go ahead and set it.
+ mDeferHidingClient = deferHidingClient;
+ setClientHidden(true);
+ }
+ return;
+ }
+
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+ Slog.v(TAG_WM, "setAppVisibility("
+ + appToken + ", visible=" + visible + "): " + appTransition
+ + " hidden=" + isHidden() + " hiddenRequested="
+ + hiddenRequested + " Callers=" + Debug.getCallers(6));
+ }
+
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.mOpeningApps.remove(this);
+ displayContent.mClosingApps.remove(this);
+ waitingToShow = false;
+ hiddenRequested = !visible;
+ mDeferHidingClient = deferHidingClient;
+
+ if (!visible) {
+ // If the app is dead while it was visible, we kept its dead window on screen.
+ // Now that the app is going invisible, we can remove it. It will be restarted
+ // if made visible again.
+ removeDeadWindows();
+ } else {
+ if (!appTransition.isTransitionSet()
+ && appTransition.isReady()) {
+ // Add the app mOpeningApps if transition is unset but ready. This means
+ // we're doing a screen freeze, and the unfreeze will wait for all opening
+ // apps to be ready.
+ displayContent.mOpeningApps.add(this);
+ }
+ startingMoved = false;
+ // If the token is currently hidden (should be the common case), or has been
+ // stopped, then we need to set up to wait for its windows to be ready.
+ if (isHidden() || mAppStopped) {
+ clearAllDrawn();
+
+ // If the app was already visible, don't reset the waitingToShow state.
+ if (isHidden()) {
+ waitingToShow = true;
+ }
+ }
+
+ // In the case where we are making an app visible but holding off for a transition,
+ // we still need to tell the client to make its windows visible so they get drawn.
+ // Otherwise, we will wait on performing the transition until all windows have been
+ // drawn, they never will be, and we are sad.
+ setClientHidden(false);
+
+ requestUpdateWallpaperIfNeeded();
+
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this);
+ mAppStopped = false;
+
+ transferStartingWindowFromHiddenAboveTokenIfNeeded();
+ }
+
+ // If we are preparing an app transition, then delay changing
+ // the visibility of this token until we execute that transition.
+ if (okToAnimate() && appTransition.isTransitionSet()) {
+ inPendingTransaction = true;
+ if (visible) {
+ displayContent.mOpeningApps.add(this);
+ mEnteringAnimation = true;
+ } else {
+ displayContent.mClosingApps.add(this);
+ mEnteringAnimation = false;
+ }
+ if (appTransition.getAppTransition()
+ == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
+ // We're launchingBehind, add the launching activity to mOpeningApps.
+ final WindowState win = getDisplayContent().findFocusedWindow();
+ if (win != null) {
+ final AppWindowToken focusedToken = win.mAppToken;
+ if (focusedToken != null) {
+ if (DEBUG_APP_TRANSITIONS) {
+ Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
+ + " adding " + focusedToken + " to mOpeningApps");
+ }
+ // Force animation to be loaded.
+ focusedToken.setHidden(true);
+ displayContent.mOpeningApps.add(focusedToken);
+ }
+ }
+ }
+ return;
+ }
+
+ commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+ updateReportedVisibilityLocked();
+ }
+
+ boolean commitVisibility(WindowManager.LayoutParams lp,
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;
@@ -417,7 +554,8 @@
// * or this is an opening app and windows are being replaced.
boolean visibilityChanged = false;
if (isHidden() == visible || (isHidden() && mIsExiting) || (visible && waitingForReplacement())) {
- final AccessibilityController accessibilityController = mService.mAccessibilityController;
+ final AccessibilityController accessibilityController =
+ mWmService.mAccessibilityController;
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
"Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout);
@@ -458,18 +596,20 @@
// We are becoming visible, so better freeze the screen with the windows that are
// getting visible so we also wait for them.
- forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true);
+ forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
}
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
- + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+ if (DEBUG_APP_TRANSITIONS) {
+ Slog.v(TAG_WM, "commitVisibility: " + this
+ + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+ }
if (changed) {
getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw();
if (performLayout) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
}
@@ -496,13 +636,13 @@
// The token was made immediately visible, there will be no entrance animation.
// We need to inform the client the enter animation was finished.
mEnteringAnimation = true;
- mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
+ mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+ token);
}
- // If we're becoming visible, immediately change client visibility as well although it
- // usually gets changed in AppWindowContainerController.setVisibility already. However,
- // there seem to be some edge cases where we change our visibility but client visibility
- // never gets updated.
+ // If we're becoming visible, immediately change client visibility as well. there seem
+ // to be some edge cases where we change our visibility but client visibility never gets
+ // updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
if (visible || !isReallyAnimating()) {
@@ -520,7 +660,7 @@
// Take the screenshot before possibly hiding the WSA, otherwise the screenshot
// will not be taken.
- mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+ mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
}
// If we are hidden but there is no delay needed we immediately
@@ -596,11 +736,6 @@
return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
}
- AppWindowContainerController getController() {
- final WindowContainerController controller = super.getController();
- return controller != null ? (AppWindowContainerController) controller : null;
- }
-
@Override
boolean isVisible() {
// If the app token isn't hidden then it is considered visible and there is no need to check
@@ -637,11 +772,11 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
- boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+ boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
getDisplayContent().mOpeningApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
- mService.mTaskSnapshotController.onAppRemoved(this);
+ mWmService.mTaskSnapshotController.onAppRemoved(this);
waitingToShow = false;
if (getDisplayContent().mClosingApps.contains(this)) {
delayed = true;
@@ -656,8 +791,8 @@
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
+ this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
- if (startingData != null && getController() != null) {
- getController().removeStartingWindow();
+ if (startingData != null) {
+ removeStartingWindow();
}
// If this window was animating, then we need to ensure that the app transition notifies
@@ -694,7 +829,7 @@
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
+ " displayId=" + dc.getDisplayId());
dc.setFocusedApp(null);
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
}
if (!delayed) {
@@ -768,9 +903,7 @@
mAppStopped = true;
destroySurfaces();
// Remove any starting window that was added for this app if they are still around.
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
}
void clearAllDrawn() {
@@ -826,9 +959,7 @@
// TODO: Something smells about the code below...Is there a better way?
if (startingWindow == win) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
} else if (mChildren.size() == 0) {
// If this is the last window and we had requested a starting transition window,
// well there is no point now.
@@ -845,9 +976,7 @@
// we need to get rid of the starting transition.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window "
+ win);
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
}
}
@@ -1014,13 +1143,17 @@
// if we got a replacement window, reset the timeout to give drawing more time
if (gotReplacementWindow) {
- mService.scheduleWindowReplacementTimeouts(this);
+ mWmService.scheduleWindowReplacementTimeouts(this);
}
checkKeyguardFlagsChanged();
}
@Override
void removeChild(WindowState child) {
+ if (!mChildren.contains(child)) {
+ // This can be true when testing.
+ return;
+ }
super.removeChild(child);
checkKeyguardFlagsChanged();
updateLetterboxSurface(child);
@@ -1042,6 +1175,20 @@
}
}
+ void reparent(TaskWindowContainerController taskController, int position) {
+ if (DEBUG_ADD_REMOVE) {
+ Slog.i(TAG_WM, "reparent: moving app token=" + this
+ + " to task=" + taskController + " at " + position);
+ }
+ final Task task = taskController.mContainer;
+ if (task == null) {
+ throw new IllegalArgumentException("reparent: could not find task="
+ + taskController);
+ }
+ reparent(task, position);
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+
void reparent(Task task, int position) {
final Task currentTask = getTask();
if (task == currentTask) {
@@ -1081,8 +1228,12 @@
super.onDisplayChanged(dc);
if (prevDc != null && prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
- if (dc.getTopStack().getTopChild().getTopChild() == this) {
- dc.setFocusedApp(this);
+ final TaskStack stack = dc.getTopStack();
+ if (stack != null) {
+ final Task task = stack.getTopChild();
+ if (task != null && task.getTopChild() == this) {
+ dc.setFocusedApp(this);
+ }
}
}
}
@@ -1122,7 +1273,7 @@
final WindowState win = mChildren.get(i);
win.onUnfreezeBounds();
}
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
void setAppLayoutChanges(int changes, String reason) {
@@ -1130,7 +1281,7 @@
final DisplayContent dc = getDisplayContent();
dc.pendingLayoutChanges |= changes;
if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
}
}
}
@@ -1151,12 +1302,12 @@
if (!hiddenRequested) {
if (!mFreezingScreen) {
mFreezingScreen = true;
- mService.registerAppFreezeListener(this);
- mService.mAppsFreezingScreen++;
- if (mService.mAppsFreezingScreen == 1) {
- mService.startFreezingDisplayLocked(0, 0, getDisplayContent());
- mService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+ mWmService.registerAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen++;
+ if (mWmService.mAppsFreezingScreen == 1) {
+ mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
+ mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
}
}
final int count = mChildren.size();
@@ -1181,15 +1332,15 @@
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
mFreezingScreen = false;
- mService.unregisterAppFreezeListener(this);
- mService.mAppsFreezingScreen--;
- mService.mLastFinishedFreezeSource = this;
+ mWmService.unregisterAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen--;
+ mWmService.mLastFinishedFreezeSource = this;
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
- mService.stopFreezingDisplayLocked();
+ mWmService.stopFreezingDisplayLocked();
}
}
@@ -1280,10 +1431,10 @@
// pending opening apps.
getDisplayContent().mOpeningApps.remove(this);
- mService.updateFocusedWindowLocked(
+ mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
getDisplayContent().setLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -1296,9 +1447,7 @@
startingData = fromToken.startingData;
fromToken.startingData = null;
fromToken.startingMoved = true;
- if (getController() != null) {
- getController().scheduleAddStartingWindow();
- }
+ scheduleAddStartingWindow();
return true;
}
@@ -1455,7 +1604,7 @@
if (mDisplayContent != null) {
mDisplayContent.setLayoutNeeded();
}
- mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+ mWmService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
// Notify the pinned stack upon all windows drawn. If there was an animation in
// progress then this signal will resume that animation.
@@ -1467,6 +1616,10 @@
}
}
+ boolean keyDispatchingTimedOut(String reason, int windowPid) {
+ return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid);
+ }
+
/**
* Updated this app token tracking states for interesting and drawn windows based on the window.
*
@@ -1485,8 +1638,8 @@
return false;
}
- if (mLastTransactionSequence != mService.mTransactionSequence) {
- mLastTransactionSequence = mService.mTransactionSequence;
+ if (mLastTransactionSequence != mWmService.mTransactionSequence) {
+ mLastTransactionSequence = mWmService.mTransactionSequence;
mNumDrawnWindows = 0;
startingDisplayed = false;
@@ -1529,8 +1682,8 @@
}
}
} else if (w.isDrawnLw()) {
- if (getController() != null) {
- getController().reportStartingWindowDrawn();
+ if (mActivityRecord != null) {
+ mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
}
startingDisplayed = true;
}
@@ -1597,6 +1750,266 @@
return this;
}
+ boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+ CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+ IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+ // If the display is frozen, we won't do anything until the actual window is
+ // displayed so there is no reason to put in the starting window.
+ if (!okToDisplay()) {
+ return false;
+ }
+
+ if (startingData != null) {
+ return false;
+ }
+
+ final WindowState mainWin = findMainWindow();
+ if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+ // App already has a visible window...why would you want a starting window?
+ return false;
+ }
+
+ final ActivityManager.TaskSnapshot snapshot =
+ mWmService.mTaskSnapshotController.getSnapshot(
+ getTask().mTaskId, getTask().mUserId,
+ false /* restoreFromDisk */, false /* reducedResolution */);
+ final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
+ allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+
+ if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+ return createSnapshot(snapshot);
+ }
+
+ // If this is a translucent window, then don't show a starting window -- the current
+ // effect (a full-screen opaque starting window that fades away to the real contents
+ // when it is ready) does not work for this.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme));
+ }
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window,
+ mWmService.mCurrentUserId);
+ if (ent == null) {
+ // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
+ // see that.
+ return false;
+ }
+ final boolean windowIsTranslucent = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+ final boolean windowIsFloating = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsFloating, false);
+ final boolean windowShowWallpaper = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+ final boolean windowDisableStarting = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowDisablePreview, false);
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Translucent=" + windowIsTranslucent
+ + " Floating=" + windowIsFloating
+ + " ShowWallpaper=" + windowShowWallpaper);
+ }
+ if (windowIsTranslucent) {
+ return false;
+ }
+ if (windowIsFloating || windowDisableStarting) {
+ return false;
+ }
+ if (windowShowWallpaper) {
+ if (getDisplayContent().mWallpaperController
+ .getWallpaperTarget() == null) {
+ // If this theme is requesting a wallpaper, and the wallpaper
+ // is not currently visible, then this effectively serves as
+ // an opaque window and our starting window transition animation
+ // can still work. We just need to make sure the starting window
+ // is also showing the wallpaper.
+ windowFlags |= FLAG_SHOW_WALLPAPER;
+ } else {
+ return false;
+ }
+ }
+
+ if (transferStartingWindow(transferFrom)) {
+ return true;
+ }
+
+ // There is no existing starting window, and we don't want to create a splash screen, so
+ // that's it!
+ if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ return false;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+ startingData = new SplashScreenStartingData(mWmService, pkg,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ getMergedOverrideConfiguration());
+ scheduleAddStartingWindow();
+ }
+ return true;
+ }
+
+
+ private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ return false;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
+ startingData = new SnapshotStartingData(mWmService, snapshot);
+ scheduleAddStartingWindow();
+ return true;
+ }
+
+ void scheduleAddStartingWindow() {
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
+ mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
+ }
+
+ private final Runnable mAddStartingWindow = new Runnable() {
+
+ @Override
+ public void run() {
+ synchronized (mWmService.mGlobalLock) {
+ // There can only be one adding request, silly caller!
+ mWmService.mAnimationHandler.removeCallbacks(this);
+ }
+
+ if (startingData == null) {
+ // Animation has been canceled... do nothing.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "startingData was nulled out before handling"
+ + " mAddStartingWindow: " + AppWindowToken.this);
+ }
+ return;
+ }
+
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData);
+ }
+
+ WindowManagerPolicy.StartingSurface surface = null;
+ try {
+ surface = startingData.createStartingSurface(AppWindowToken.this);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when adding starting window", e);
+ }
+ if (surface != null) {
+ boolean abort = false;
+ synchronized (mWmService.mGlobalLock) {
+ // If the window was successfully added, then
+ // we need to remove it.
+ if (removed || startingData == null) {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Aborted starting " + AppWindowToken.this
+ + ": removed=" + removed + " startingData=" + startingData);
+ }
+ startingWindow = null;
+ startingData = null;
+ abort = true;
+ } else {
+ startingSurface = surface;
+ }
+ if (DEBUG_STARTING_WINDOW && !abort) {
+ Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
+ + startingWindow + " startingView=" + startingSurface);
+ }
+ }
+ if (abort) {
+ surface.remove();
+ }
+ } else if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this);
+ }
+ }
+ };
+
+ private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+ ActivityManager.TaskSnapshot snapshot) {
+ if (getDisplayContent().mAppTransition.getAppTransition()
+ == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ // TODO(b/34099271): Remove this statement to add back the starting window and figure
+ // out why it causes flickering, the starting window appears over the thumbnail while
+ // the docked from recents transition occurs
+ return STARTING_WINDOW_TYPE_NONE;
+ } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else if (taskSwitch && allowTaskSnapshot) {
+ return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+ : snapshotOrientationSameAsTask(snapshot) || fromRecents
+ ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ return STARTING_WINDOW_TYPE_NONE;
+ }
+ }
+
+
+ private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ return false;
+ }
+ return getTask().getConfiguration().orientation == snapshot.getOrientation();
+ }
+
+ void removeStartingWindow() {
+ if (startingWindow == null) {
+ if (startingData != null) {
+ // Starting window has not been added yet, but it is scheduled to be added.
+ // Go ahead and cancel the request.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "Clearing startingData for token=" + this);
+ }
+ startingData = null;
+ }
+ return;
+ }
+
+ final WindowManagerPolicy.StartingSurface surface;
+ if (startingData != null) {
+ surface = startingSurface;
+ startingData = null;
+ startingSurface = null;
+ startingWindow = null;
+ startingDisplayed = false;
+ if (surface == null) {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
+ + "remove");
+ }
+ return;
+ }
+ } else {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
+ + this);
+ }
+ return;
+ }
+
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "Schedule remove starting " + this
+ + " startingWindow=" + startingWindow
+ + " startingView=" + startingSurface
+ + " Callers=" + Debug.getCallers(5));
+ }
+
+ // Use the same thread to remove the window as we used to add it, as otherwise we end up
+ // with things in the view hierarchy being called from different threads.
+ mWmService.mAnimationHandler.post(() -> {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
+ try {
+ surface.remove();
+ } catch (Exception e) {
+ Slog.w(TAG_WM, "Exception when removing starting window", e);
+ }
+ });
+ }
+
@Override
boolean fillsParent() {
return mFillsParent;
@@ -1643,7 +2056,7 @@
final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
|| containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
- mService.notifyKeyguardFlagsChanged(null /* callback */,
+ mWmService.notifyKeyguardFlagsChanged(null /* callback */,
getDisplayContent().getDisplayId());
}
mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
@@ -1750,10 +2163,8 @@
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
final SurfaceControl.Builder builder = makeAnimationLeash()
.setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - animation-bounds")
- .setSize(getSurfaceWidth(), getSurfaceHeight());
+ .setName(getSurfaceControl() + " - animation-bounds");
final SurfaceControl boundsLayer = builder.build();
- t.setWindowCrop(boundsLayer, getSurfaceWidth(), getSurfaceHeight());
t.show(boundsLayer);
return boundsLayer;
}
@@ -1767,9 +2178,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.
@@ -1782,10 +2193,22 @@
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) {
- if (mService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
+ if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped."
+ " atoken=" + this);
@@ -1819,7 +2242,7 @@
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appStackClipMode,
true /* isAppAnimation */),
- mService.mSurfaceAnimationRunner);
+ mWmService.mSurfaceAnimationRunner);
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
mNeedsZBoost = true;
}
@@ -1902,7 +2325,7 @@
final int containingWidth = frame.width();
final int containingHeight = frame.height();
a.initialize(containingWidth, containingHeight, width, height);
- a.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+ a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
}
return a;
}
@@ -2107,7 +2530,7 @@
return;
}
final Rect frame = win.getFrameLw();
- final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
+ final int thumbnailDrawableRes = getTask().mUserId == mWmService.mCurrentUserId
? R.drawable.ic_account_circle
: R.drawable.ic_corp_badge;
final GraphicBuffer thumbnail =
@@ -2211,9 +2634,6 @@
if (mPendingRelaunchCount != 0) {
pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
}
- if (getController() != null) {
- pw.print(prefix); pw.print("controller="); pw.println(getController());
- }
if (mRemovingFromDisplay) {
pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
}
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9633864..c90f5bf 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -48,7 +48,6 @@
surface = dc.makeOverlay()
.setName("BlackSurface")
- .setSize(w, h)
.setColorLayer(true)
.setParent(null) // TODO: Work-around for b/69259549
.build();
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2a216ab..c3d6211 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -69,7 +69,7 @@
try {
ctrl = dc.makeOverlay()
.setName("CircularDisplayMask")
- .setSize(mScreenSize.x, mScreenSize.y) // not a typo
+ .setBufferSize(mScreenSize.x, mScreenSize.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
.build();
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/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index cc14afc..aea071f 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -131,7 +131,7 @@
if (!mDimming) {
dimAnimatable.getPendingTransaction().destroy(mDimLayer);
}
- }, mHost.mService);
+ }, mHost.mWmService);
}
}
@@ -308,7 +308,6 @@
return false;
} else {
// TODO: Once we use geometry from hierarchy this falls away.
- t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
if (!mDimState.isVisible) {
@@ -334,7 +333,7 @@
SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
- mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
+ mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
private long getDimDuration(WindowContainer container) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 886b2ff..aba2eb3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,6 +29,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.InsetsState.TYPE_IME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -78,7 +79,6 @@
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
-import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -100,6 +100,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
@@ -124,6 +125,7 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -151,6 +153,7 @@
import android.view.Gravity;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.InsetsState.InternalInsetType;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -162,6 +165,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.TriConsumer;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.DisplayRotationUtil;
import com.android.server.wm.utils.RotationCache;
@@ -205,21 +209,21 @@
/** 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(mService);
+ private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
// Contains all non-app window containers that should be displayed above the app containers
// (e.g. Status bar)
private final AboveAppWindowContainers mAboveAppWindowsContainers =
- new AboveAppWindowContainers("mAboveAppWindowsContainers", mService);
+ new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
// Contains all non-app window containers that should be displayed below the app containers
// (e.g. Wallpaper).
private final NonAppWindowContainers mBelowAppWindowsContainers =
- new NonAppWindowContainers("mBelowAppWindowsContainers", mService);
+ new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
// WindowContainer which is omitted from screen magnification, as the IME is never magnified.
private final NonMagnifiableWindowContainers mImeWindowsContainers =
- new NonMagnifiableWindowContainers("mImeWindowsContainers", mService);
+ new NonMagnifiableWindowContainers("mImeWindowsContainers", mWmService);
private WindowState mTmpWindow;
private WindowState mTmpWindow2;
@@ -352,6 +356,14 @@
int pendingLayoutChanges;
int mDeferredRotationPauseCount;
+ /**
+ * Used to gate application window layout until we have sent the complete configuration.
+ * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+ * we want to replace this flag with a mechanism that will confirm the configuration
+ * applied by the client is the one expected by the system server.
+ */
+ boolean mWaitingForConfig;
+
// TODO(multi-display): remove some of the usages.
@VisibleForTesting
boolean isDefaultDisplay;
@@ -462,14 +474,6 @@
private SurfaceControl mWindowingLayer;
/**
- * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
- * <p>
- * For these surfaces currently we use a surface based on the larger of width or height so we
- * don't have to resize when rotating the display.
- */
- private int mSurfaceSize;
-
- /**
* Sequence number for the current layout pass.
*/
int mLayoutSeq = 0;
@@ -507,6 +511,8 @@
private final PointerEventDispatcher mPointerEventDispatcher;
+ private final InsetsStateController mInsetsStateController;
+
// Last systemUiVisibility we received from status bar.
private int mLastStatusBarVisibility = 0;
// Last systemUiVisibility we dispatched to windows.
@@ -520,7 +526,7 @@
if (w.performShowLocked()) {
pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats(
"updateWindowsAndWallpaperLocked 5", pendingLayoutChanges);
}
}
@@ -551,7 +557,7 @@
private final Consumer<WindowState> mScheduleToastTimeout = w -> {
final int lostFocusUid = mTmpWindow.mOwnerUid;
- final Handler handler = mService.mH;
+ final Handler handler = mWmService.mH;
if (w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == lostFocusUid) {
if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, w)) {
handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, w),
@@ -613,7 +619,7 @@
private final Consumer<WindowState> mPerformLayout = w -> {
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
- final boolean gone = (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w))
+ final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
|| w.isGoneForLayoutLw();
if (DEBUG_LAYOUT && !w.mLayoutAttached) {
@@ -681,7 +687,7 @@
// If this view is GONE, then skip it -- keep the current frame, and let the caller
// know so they can ignore it if they want. (We do the normal layout for INVISIBLE
// windows, since that means "perform layout as normal, just don't display").
- if (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) {
+ if (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w)) {
return;
}
if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
@@ -716,10 +722,10 @@
mInputMethodTarget);
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
- final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+ final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
final boolean obscuredChanged = w.mObscured !=
mTmpApplySurfaceChangesTransactionState.obscured;
- final RootWindowContainer root = mService.mRoot;
+ final RootWindowContainer root = mWmService.mRoot;
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -810,7 +816,7 @@
}
if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
- mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
+ mWmService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
}
w.updateResizingWindowIfNeeded();
@@ -835,7 +841,7 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
- mWallpaperController = new WallpaperController(mService, this);
+ mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -852,8 +858,8 @@
mBoundsAnimationController = new BoundsAnimationController(service.mContext,
mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
- if (mService.mInputManager != null) {
- final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+ if (mWmService.mInputManager != null) {
+ final InputChannel inputChannel = mWmService.mInputManager.monitorInput("Display "
+ mDisplayId, mDisplayId);
mPointerEventDispatcher = inputChannel != null
? new PointerEventDispatcher(inputChannel) : null;
@@ -865,29 +871,18 @@
if (isDefaultDisplay) {
// The policy may be invoked right after here, so it requires the necessary default
// fields of this display content.
- mService.mPolicy.setDefaultDisplay(this);
+ mWmService.mPolicy.setDefaultDisplay(this);
}
- if (mService.mDisplayReady) {
+ if (mWmService.mDisplayReady) {
mDisplayPolicy.onConfigurationChanged();
}
- if (mService.mSystemReady) {
+ if (mWmService.mSystemReady) {
mDisplayPolicy.systemReady();
}
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- // We use this as our arbitrary surface size for buffer-less parents
- // that don't impose cropping on their children. It may need to be larger
- // than the display size because fullscreen windows can be shifted offscreen
- // due to surfaceInsets. 2 times the largest display dimension feels like an
- // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
- // layers the ability to match their parent sizes and be able to skip
- // such arbitrary size settings.
- mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
-
- final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
- .setSize(mSurfaceSize, mSurfaceSize)
- .setOpaque(true);
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true);
mWindowingLayer = b.setName("Display Root").build();
mOverlayLayer = b.setName("Display Overlays").build();
@@ -906,19 +901,20 @@
super.addChild(mImeWindowsContainers, null);
// Add itself as a child to the root container.
- mService.mRoot.addChild(this, null);
+ mWmService.mRoot.addChild(this, null);
// TODO(b/62541591): evaluate whether this is the best spot to declare the
// {@link DisplayContent} ready for use.
mDisplayReady = true;
- mService.mAnimator.addDisplayLocked(mDisplayId);
+ mWmService.mAnimator.addDisplayLocked(mDisplayId);
mInputMonitor = new InputMonitor(service, mDisplayId);
+ mInsetsStateController = new InsetsStateController(this);
}
boolean isReady() {
// The display is ready when the system and the individual display are both ready.
- return mService.mDisplayReady && mDisplayReady;
+ return mWmService.mDisplayReady && mDisplayReady;
}
int getDisplayId() {
@@ -938,7 +934,7 @@
}
private void addWindowToken(IBinder binder, WindowToken token) {
- final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
+ final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
@@ -1050,6 +1046,23 @@
return mDisplayRotation;
}
+ /**
+ * Marks a window as providing insets for the rest of the windows in the system.
+ *
+ * @param type The type of inset this window provides.
+ * @param win The window.
+ * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
+ * the window should be taken.
+ */
+ void setInsetProvider(@InternalInsetType int type, WindowState win,
+ @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+ mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
+ }
+
+ InsetsStateController getInsetsStateController() {
+ return mInsetsStateController;
+ }
+
@VisibleForTesting
void setDisplayRotation(DisplayRotation displayRotation) {
mDisplayRotation = displayRotation;
@@ -1127,7 +1140,7 @@
/** Notify the configuration change of this display. */
void sendNewConfiguration() {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
+ mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
}
/**
@@ -1202,7 +1215,7 @@
}
screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
// Rotation updates cannot be performed while the previous rotation change
// animation is still in progress. Skip this update. We will try updating
@@ -1210,7 +1223,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
return false;
}
- if (mService.mDisplayFrozen) {
+ if (mWmService.mDisplayFrozen) {
// Even if the screen rotation animation has finished (e.g. isAnimating
// returns false), there is still some time where we haven't yet unfrozen
// the display. We also need to abort rotation here.
@@ -1220,7 +1233,7 @@
}
}
- if (!mService.mDisplayEnabled) {
+ if (!mWmService.mDisplayEnabled) {
// No point choosing a rotation if the display is not enabled.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
return false;
@@ -1251,8 +1264,8 @@
if (hasPinnedStack()) {
mayRotateSeamlessly = false;
}
- for (int i = 0; i < mService.mSessions.size(); i++) {
- if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
+ for (int i = 0; i < mWmService.mSessions.size(); i++) {
+ if (mWmService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
mayRotateSeamlessly = false;
break;
}
@@ -1284,14 +1297,14 @@
+ (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
- mService.mWaitingForConfig = true;
+ mWaitingForConfig = true;
}
mRotation = rotation;
mAltOrientation = altOrientation;
- mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
- mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+ mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
+ mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
this, WINDOW_FREEZE_TIMEOUT_DURATION);
setLayoutNeeded();
@@ -1299,14 +1312,14 @@
mDisplayPolicy.selectRotationAnimationLw(anim);
if (!rotateSeamlessly) {
- mService.startFreezingDisplayLocked(anim[0], anim[1], this);
+ mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
// startFreezingDisplayLocked can reset the ScreenRotationAnimation.
} else {
// The screen rotation animation uses a screenshot to freeze the screen
// while windows resize underneath.
// When we are rotating seamlessly, we allow the elements to transition
// to their rotated state independently and without a freeze required.
- mService.startSeamlessRotation();
+ mWmService.startSeamlessRotation();
}
return true;
@@ -1321,9 +1334,9 @@
*/
void applyRotationLocked(final int oldRotation, final int rotation) {
mDisplayRotation.setRotation(rotation);
- final boolean rotateSeamlessly = mService.isRotatingSeamlessly();
+ final boolean rotateSeamlessly = mWmService.isRotatingSeamlessly();
ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
- ? null : mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ ? null : mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -1336,9 +1349,9 @@
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
if (screenRotationAnimation.setRotation(getPendingTransaction(), rotation,
- MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
+ MAX_ANIMATION_DURATION, mWmService.getTransitionAnimationScaleLocked(),
mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
}
@@ -1347,27 +1360,27 @@
rotateSeamlessly);
}, true /* traverseTopToBottom */);
- mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
+ mWmService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
scheduleAnimation();
forAllWindows(w -> {
if (w.mHasSurface && !rotateSeamlessly) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
w.setOrientationChanging(true);
- mService.mRoot.mOrientationChangeComplete = false;
+ mWmService.mRoot.mOrientationChangeComplete = false;
w.mLastFreezeDuration = 0;
}
w.mReportOrientationChanged = true;
}, true /* traverseTopToBottom */);
if (rotateSeamlessly) {
- mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+ mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
}
- for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
+ for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
final WindowManagerService.RotationWatcher rotationWatcher
- = mService.mRotationWatchers.get(i);
+ = mWmService.mRotationWatchers.get(i);
if (rotationWatcher.mDisplayId == mDisplayId) {
try {
rotationWatcher.mWatcher.onRotationChanged(rotation);
@@ -1380,9 +1393,9 @@
// TODO (multi-display): Magnification is supported only for the default display.
// Announce rotation only if we will not animate as we already have the
// windows in final state. Otherwise, we make this call at the rotation end.
- if (screenRotationAnimation == null && mService.mAccessibilityController != null
+ if (screenRotationAnimation == null && mWmService.mAccessibilityController != null
&& isDefaultDisplay) {
- mService.mAccessibilityController.onRotationChangedLocked(this);
+ mWmService.mAccessibilityController.onRotationChangedLocked(this);
}
}
@@ -1416,9 +1429,9 @@
Slog.d(TAG,
"Registering PointerEventListener for DisplayId: " + mDisplayId);
}
- mTapDetector = new TaskTapPointerEventListener(mService, this);
+ mTapDetector = new TaskTapPointerEventListener(mWmService, this);
registerPointerEventListener(mTapDetector);
- registerPointerEventListener(mService.mMousePositionTracker);
+ registerPointerEventListener(mWmService.mMousePositionTracker);
}
}
@@ -1483,7 +1496,7 @@
// because we don't want letter-/pillar-boxing during resize.
final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
? mDisplayInfo : null;
- mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
+ mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
overrideDisplayInfo);
mBaseDisplayRect.set(0, 0, dw, dh);
@@ -1533,6 +1546,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;
@@ -1569,10 +1583,10 @@
config.densityDpi = displayInfo.logicalDensityDpi;
config.colorMode =
- ((displayInfo.isHdr() && mService.hasHdrSupport())
+ ((displayInfo.isHdr() && mWmService.hasHdrSupport())
? Configuration.COLOR_MODE_HDR_YES
: Configuration.COLOR_MODE_HDR_NO)
- | (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport()
+ | (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
: Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);
@@ -1584,7 +1598,7 @@
int keyboardPresence = 0;
int navigationPresence = 0;
- final InputDevice[] devices = mService.mInputManager.getInputDevices();
+ final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
final int len = devices != null ? devices.length : 0;
for (int i = 0; i < len; i++) {
InputDevice device = devices[i];
@@ -1595,7 +1609,7 @@
WindowManagerPolicy.PRESENCE_INTERNAL;
// TODO(multi-display): Configure on per-display basis.
- if (mService.mIsTouchDevice) {
+ if (mWmService.mIsTouchDevice) {
if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
InputDevice.SOURCE_TOUCHSCREEN) {
config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
@@ -1620,7 +1634,7 @@
}
}
- if (config.navigation == Configuration.NAVIGATION_NONAV && mService.mHasPermanentDpad) {
+ if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
config.navigation = Configuration.NAVIGATION_DPAD;
navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
}
@@ -1628,10 +1642,10 @@
// Determine whether a hard keyboard is available and enabled.
// TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
- if (hardKeyboardAvailable != mService.mHardKeyboardAvailable) {
- mService.mHardKeyboardAvailable = hardKeyboardAvailable;
- mService.mH.removeMessages(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
+ mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
+ mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
}
mDisplayPolicy.updateConfigurationDependentBehaviors();
@@ -1640,7 +1654,7 @@
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
- mService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
+ mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
@@ -1937,7 +1951,18 @@
@Override
void onAppTransitionDone() {
super.onAppTransitionDone();
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
+ }
+
+ @Override
+ public void setWindowingMode(int windowingMode) {
+ super.setWindowingMode(windowingMode);
+ super.setDisplayWindowingMode(windowingMode);
+ }
+
+ @Override
+ void setDisplayWindowingMode(int windowingMode) {
+ setWindowingMode(windowingMode);
}
/**
@@ -1989,9 +2014,9 @@
@Override
int getOrientation() {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
- if (mService.mDisplayFrozen) {
+ if (mWmService.mDisplayFrozen) {
if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ " is frozen, return " + mLastWindowForcedOrientation);
@@ -2032,7 +2057,7 @@
}
void initializeDisplayBaseInfo() {
- final DisplayManagerInternal displayManagerInternal = mService.mDisplayManagerInternal;
+ final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal;
if (displayManagerInternal != null) {
// Bootstrap the default logical display from the display manager.
final DisplayInfo newDisplayInfo = displayManagerInternal.getDisplayInfo(mDisplayId);
@@ -2055,7 +2080,7 @@
*/
private void updateBaseDisplayMetricsIfNeeded() {
// Get real display metrics without overrides from WM.
- mService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
+ mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
final int orientation = mDisplayInfo.rotation;
final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
@@ -2084,7 +2109,7 @@
mInitialDisplayHeight = newHeight;
mInitialDisplayDensity = newDensity;
mInitialDisplayCutout = newCutout;
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
}
}
@@ -2135,9 +2160,9 @@
void setForcedDensity(int density, int userId) {
final boolean clear = density == mInitialDisplayDensity;
final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
- if (mService.mCurrentUserId == userId || updateCurrent) {
+ if (mWmService.mCurrentUserId == userId || updateCurrent) {
mBaseDisplayDensity = density;
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
}
if (updateCurrent) {
// We are applying existing settings so no need to save it again.
@@ -2147,7 +2172,7 @@
if (density == mInitialDisplayDensity) {
density = 0;
}
- mService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
+ mWmService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
}
/** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */
@@ -2158,9 +2183,9 @@
mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO);
Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto"));
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
- mService.mDisplayWindowSettings.setForcedScalingMode(this, mode);
+ mWmService.mDisplayWindowSettings.setForcedScalingMode(this, mode);
}
/** If the given width and height equal to initial size, the setting will be cleared. */
@@ -2177,12 +2202,12 @@
Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
updateBaseDisplayMetrics(width, height, mBaseDisplayDensity);
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
if (clear) {
width = height = 0;
}
- mService.mDisplayWindowSettings.setForcedSize(this, width, height);
+ mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
}
void getStableRect(Rect out) {
@@ -2193,7 +2218,7 @@
if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+ mDisplayId);
- final TaskStack stack = new TaskStack(mService, stackId, controller);
+ final TaskStack stack = new TaskStack(mWmService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
return stack;
}
@@ -2349,7 +2374,7 @@
@Override
void switchUser() {
super.switchUser();
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
}
private void resetAnimationBackgroundAnimator() {
@@ -2377,24 +2402,24 @@
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
- mService.stopFreezingDisplayLocked();
+ mWmService.stopFreezingDisplayLocked();
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
if (mPointerEventDispatcher != null && mTapDetector != null) {
unregisterPointerEventListener(mTapDetector);
- unregisterPointerEventListener(mService.mMousePositionTracker);
+ unregisterPointerEventListener(mWmService.mMousePositionTracker);
mTapDetector = null;
}
- mService.mAnimator.removeDisplayLocked(mDisplayId);
+ mWmService.mAnimator.removeDisplayLocked(mDisplayId);
mWindowingLayer.release();
mOverlayLayer.release();
} finally {
mDisplayReady = false;
mRemovingDisplay = false;
}
+
mDisplayPolicy.onDisplayRemoved();
- mInputMonitor.onRemoved();
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
/** Returns true if a removal action is still being deferred. */
@@ -2471,7 +2496,7 @@
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
+ final TaskStack imeTargetStack = mWmService.getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
imeTargetStack.getDockSide() : DOCKED_INVALID;
final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
@@ -2598,13 +2623,12 @@
mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
proto.write(ROTATION, mRotation);
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null) {
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
mAppTransition.writeToProto(proto, APP_TRANSITION);
- proto.write(SURFACE_SIZE, mSurfaceSize);
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -2725,6 +2749,8 @@
mDisplayRotation.dump(prefix, pw);
pw.println();
mInputMonitor.dump(pw, " ");
+ pw.println();
+ mInsetsStateController.dump(prefix, pw);
}
@Override
@@ -2842,11 +2868,11 @@
}
if (imWindowChanged) {
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
setLayoutNeeded();
}
- if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+ if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+ mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
+ " Callers=" + Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
@@ -2885,7 +2911,7 @@
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayout(true /*initial*/, updateInputWindows);
} else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
- mService.mRoot.performSurfacePlacement(false);
+ mWmService.mRoot.performSurfacePlacement(false);
}
}
@@ -2950,16 +2976,16 @@
// moving containers or resizing them. Need to investigate the best way to have it automatically
// happen so we don't run into issues with programmers forgetting to do it.
void layoutAndAssignWindowLayersIfNeeded() {
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
setLayoutNeeded();
- if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ if (!mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
assignWindowLayers(false /* setLayoutNeeded */);
}
mInputMonitor.setUpdateInputWindowsNeededLw();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
@@ -2972,14 +2998,14 @@
if (wsa.mSurfaceController == null) {
return;
}
- if (!mService.mSessions.contains(wsa.mSession)) {
+ if (!mWmService.mSessions.contains(wsa.mSession)) {
Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
+ w + " surface=" + wsa.mSurfaceController
+ " token=" + w.mToken
+ " pid=" + w.mSession.mPid
+ " uid=" + w.mSession.mUid);
wsa.destroySurface();
- mService.mForceRemoves.add(w);
+ mWmService.mForceRemoves.add(w);
mTmpWindow = w;
} else if (w.mAppToken != null && w.mAppToken.isClientHidden()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
@@ -3003,10 +3029,12 @@
// Update display configuration for IME process.
if (mInputMethodWindow != null) {
final int imePid = mInputMethodWindow.mSession.mPid;
- mService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
+ mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
mInputMethodWindow.getDisplayId());
}
computeImeTarget(true /* updateImeTarget */);
+ mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win,
+ null /* frameProvider */);
}
/**
@@ -3222,7 +3250,7 @@
* Starts the Keyguard exit animation on all windows that don't belong to an app token.
*/
void startKeyguardExitOnNonAppWindows(boolean onWallpaper, boolean goingToShade) {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
forAllWindows(w -> {
if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
&& w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
@@ -3251,7 +3279,7 @@
} else if (w.mAttrs.type == TYPE_WALLPAPER) {
mHaveWallpaper = true;
} else if (w.mAttrs.type == TYPE_STATUS_BAR) {
- mHaveKeyguard = mService.mPolicy.isKeyguardDrawnLw();
+ mHaveKeyguard = mWmService.mPolicy.isKeyguardDrawnLw();
}
}
return false;
@@ -3264,28 +3292,28 @@
// if the wallpaper service is disabled on the device, we're never going to have
// wallpaper, don't bother waiting for it
- boolean wallpaperEnabled = mService.mContext.getResources().getBoolean(
+ boolean wallpaperEnabled = mWmService.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableWallpaperService)
- && mService.mContext.getResources().getBoolean(
+ && mWmService.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_checkWallpaperAtBoot)
- && !mService.mOnlyCore;
+ && !mWmService.mOnlyCore;
if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM,
- "******** booted=" + mService.mSystemBooted
- + " msg=" + mService.mShowingBootMessages
+ "******** booted=" + mWmService.mSystemBooted
+ + " msg=" + mWmService.mShowingBootMessages
+ " haveBoot=" + mHaveBootMsg + " haveApp=" + mHaveApp
+ " haveWall=" + mHaveWallpaper + " wallEnabled=" + wallpaperEnabled
+ " haveKeyguard=" + mHaveKeyguard);
// If we are turning on the screen to show the boot message, don't do it until the boot
// message is actually displayed.
- if (!mService.mSystemBooted && !mHaveBootMsg) {
+ if (!mWmService.mSystemBooted && !mHaveBootMsg) {
return true;
}
// If we are turning on the screen after the boot is completed normally, don't do so until
// we have the application and wallpaper.
- if (mService.mSystemBooted
+ if (mWmService.mSystemBooted
&& ((!mHaveApp && !mHaveKeyguard) || (wallpaperEnabled && !mHaveWallpaper))) {
return true;
}
@@ -3351,7 +3379,7 @@
mLastDispatchedSystemUiVisibility = visibility;
if (isDefaultDisplay) {
- mService.mInputManager.setSystemUiVisibility(visibility);
+ mWmService.mInputManager.setSystemUiVisibility(visibility);
}
updateSystemUiVisibility(visibility, globalDiff);
return true;
@@ -3380,13 +3408,13 @@
void reevaluateStatusBarVisibility() {
int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
if (updateStatusBarVisibilityLocked(visibility)) {
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
}
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
- mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
+ mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
forAllWindows(w -> {
if (!w.getOrientationChanging()) {
@@ -3394,28 +3422,28 @@
}
w.orientationChangeTimedOut();
w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mService.mDisplayFreezeTime);
+ - mWmService.mDisplayFreezeTime);
Slog.w(TAG_WM, "Force clearing orientation change: " + w);
}, true /* traverseTopToBottom */);
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
void waitForAllWindowsDrawn() {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
forAllWindows(w -> {
final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
w.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
w.resetLastContentInsets();
- mService.mWaitingForDrawn.add(w);
+ mWmService.mWaitingForDrawn.add(w);
}
}, true /* traverseTopToBottom */);
}
// TODO: Super crazy long method that should be broken down...
void applySurfaceChangesTransaction(boolean recoveringMemory) {
- final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+ final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
mTmpUpdateAllDrawn.clear();
@@ -3462,6 +3490,7 @@
pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
"after finishPostLayoutPolicyLw", pendingLayoutChanges);
+ mInsetsStateController.onPostLayout();
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
@@ -3471,7 +3500,7 @@
prepareSurfaces();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
- mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
+ mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
@@ -3480,7 +3509,7 @@
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
- mService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
+ mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
}
while (!mTmpUpdateAllDrawn.isEmpty()) {
@@ -3529,10 +3558,6 @@
}
}
- int getSurfaceSize() {
- return mSurfaceSize;
- }
-
void performLayout(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
@@ -3582,7 +3607,7 @@
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+ mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
/**
@@ -3592,7 +3617,7 @@
* @param config of the output bitmap
*/
Bitmap screenshotDisplayLocked(Bitmap.Config config) {
- if (!mService.mPolicy.isScreenOn()) {
+ if (!mWmService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
}
@@ -3620,7 +3645,7 @@
convertCropForSurfaceFlinger(frame, rot, dw, dh);
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
final boolean inRotation = screenRotationAnimation != null &&
screenRotationAnimation.isAnimating();
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
@@ -3675,11 +3700,12 @@
}
mTmpWindow = w;
w.setDisplayLayoutNeeded();
- mService.markForSeamlessRotation(w, false);
+ w.finishSeamlessRotation(true /* timeout */);
+ mWmService.markForSeamlessRotation(w, false);
}, true /* traverseTopToBottom */);
if (mTmpWindow != null) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -3707,20 +3733,20 @@
@Override
void onDescendantOverrideConfigurationChanged() {
setLayoutNeeded();
- mService.requestTraversal();
+ mWmService.requestTraversal();
}
boolean okToDisplay() {
if (mDisplayId == DEFAULT_DISPLAY) {
- return !mService.mDisplayFrozen
- && mService.mDisplayEnabled && mService.mPolicy.isScreenOn();
+ return !mWmService.mDisplayFrozen
+ && mWmService.mDisplayEnabled && mWmService.mPolicy.isScreenOn();
}
return mDisplayInfo.state == Display.STATE_ON;
}
boolean okToAnimate() {
return okToDisplay() &&
- (mDisplayId != DEFAULT_DISPLAY || mService.mPolicy.okToAnimate());
+ (mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate());
}
static final class TaskForResizePointSearchResult {
@@ -3938,7 +3964,7 @@
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
// Re-set the split-screen create mode whenever the split-screen stack is removed.
- mService.setDockedStackCreateStateLocked(
+ mWmService.setDockedStackCreateStateLocked(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
@@ -4156,7 +4182,7 @@
}
final int orientation = super.getOrientation();
- boolean isCar = mService.mContext.getPackageManager().hasSystemFeature(
+ boolean isCar = mWmService.mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
if (isCar) {
// In a car, you cannot physically rotate the screen, so it doesn't make sense to
@@ -4337,10 +4363,10 @@
wt.assignLayer(t, j);
wt.assignChildLayers(t);
- int layer = mService.mPolicy.getWindowLayerFromTypeLw(
+ int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
wt.windowType, wt.mOwnerCanManageAppTokens);
- if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw(
+ if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
TYPE_INPUT_METHOD_DIALOG, true)) {
imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
needAssignIme = false;
@@ -4363,9 +4389,9 @@
*/
private final Comparator<WindowToken> mWindowComparator = (token1, token2) ->
// Tokens with higher base layer are z-ordered on-top.
- mService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
+ mWmService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
token1.mOwnerCanManageAppTokens)
- < mService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
+ < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
token2.mOwnerCanManageAppTokens) ? -1 : 1;
private final Predicate<WindowState> mGetOrientingWindow = w -> {
@@ -4395,7 +4421,7 @@
@Override
int getOrientation() {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
// Find a window requesting orientation.
final WindowState win = getWindow(mGetOrientingWindow);
@@ -4403,7 +4429,7 @@
final int req = win.mAttrs.screenOrientation;
if (policy.isKeyguardHostWindow(win.mAttrs)) {
mLastKeyguardForcedOrientation = req;
- if (mService.mKeyguardGoingAway) {
+ if (mWmService.mKeyguardGoingAway) {
// Keyguard can't affect the orientation if it is going away...
mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
return SCREEN_ORIENTATION_UNSET;
@@ -4461,7 +4487,7 @@
};
SurfaceControl.Builder makeSurface(SurfaceSession s) {
- return mService.makeSurfaceBuilder(s)
+ return mWmService.makeSurfaceBuilder(s)
.setParent(mWindowingLayer);
}
@@ -4473,9 +4499,7 @@
@Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
SurfaceSession s = child != null ? child.getSession() : getSession();
- final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
- b.setSize(mSurfaceSize, mSurfaceSize);
-
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s);
if (child == null) {
return b;
}
@@ -4491,7 +4515,7 @@
* and other potpourii.
*/
SurfaceControl.Builder makeOverlay() {
- return mService.makeSurfaceBuilder(mSession)
+ return mWmService.makeSurfaceBuilder(mSession)
.setParent(mOverlayLayer);
}
@@ -4590,7 +4614,7 @@
@Override
void prepareSurfaces() {
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
mPendingTransaction.setMatrix(mWindowingLayer,
@@ -4678,7 +4702,7 @@
+ mDisplayId + " Callers=" + Debug.getCallers(5));
}
mAppTransition.setReady();
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
}
@@ -4708,7 +4732,7 @@
mWallpaperMayChange = true;
// Since the window list has been rebuilt, focus might have to be recomputed since the
// actual order of windows might have changed again.
- mService.mFocusMayChange = true;
+ mWmService.mFocusMayChange = true;
pendingLayoutChanges |= changes;
}
@@ -4721,7 +4745,19 @@
return mDisplay.supportsSystemDecorations()
// TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
// (b/114338689) whenever vr 2d display id is set.
- || mDisplayId == mService.mVr2dDisplayId
- || mService.mForceDesktopModeOnExternalDisplays;
+ || mDisplayId == mWmService.mVr2dDisplayId
+ || mWmService.mForceDesktopModeOnExternalDisplays;
+ }
+
+ /**
+ * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
+ * {@link #mOverlayLayer} to the specified surfaceControl.
+ *
+ * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+ * surfaces will be re-parented to.
+ */
+ void reparentDisplayContent(IBinder surfaceControlHandle) {
+ mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
+ .reparent(mOverlayLayer, surfaceControlHandle);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c16f95e..d4bd91b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -44,7 +45,6 @@
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
@@ -128,6 +128,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -221,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;
@@ -522,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.
@@ -533,6 +536,7 @@
mHasNavigationBar = true;
}
} else {
+ mHasStatusBar = false;
mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations();
}
}
@@ -588,6 +592,10 @@
return mHasNavigationBar;
}
+ public boolean hasStatusBar() {
+ return mHasStatusBar;
+ }
+
public boolean navigationBarCanMove() {
return mNavigationBarCanMove;
}
@@ -804,6 +812,11 @@
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(win);
}
+ mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win,
+ (displayFrames, windowState, rect) -> {
+ rect.top = 0;
+ rect.bottom = getStatusBarHeight(displayFrames);
+ });
break;
case TYPE_NAVIGATION_BAR:
mContext.enforceCallingOrSelfPermission(
@@ -818,6 +831,8 @@
mNavigationBarController.setWindow(win);
mNavigationBarController.setOnBarVisibilityChangedListener(
mNavBarVisibilityListener, true);
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,
+ win, null /* frameProvider */);
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
case TYPE_NAVIGATION_BAR_PANEL:
@@ -845,9 +860,11 @@
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(null);
}
+ mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null);
} else if (mNavigationBar == win) {
mNavigationBar = null;
mNavigationBarController.setWindow(null);
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null);
}
if (mLastFocusedWindow == win) {
mLastFocusedWindow = null;
@@ -855,6 +872,11 @@
mScreenDecorWindows.remove(win);
}
+ private int getStatusBarHeight(DisplayFrames displayFrames) {
+ return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
+ displayFrames.mDisplayCutoutSafe.top);
+ }
+
/**
* Control the animation to run when a window's state changes. Return a
* non-0 number to force the animation to a specific resource ID, or 0
@@ -2306,12 +2328,6 @@
&& windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
-
- // Take note if a window wants to acquire a sleep token.
- if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
- && win.canAcquireSleepToken()) {
- mWindowSleepTokenNeeded = true;
- }
}
/**
@@ -2484,12 +2500,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] =
@@ -2825,6 +2848,9 @@
return 0;
}
+ mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(
+ mTopFullscreenOpaqueWindowState);
+
int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index f1d1e49..7aabc15 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,6 +31,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.power.V1_0.PowerHint;
+import android.net.Uri;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -57,6 +59,7 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final DisplayPolicy mDisplayPolicy;
+ private final DisplayWindowSettings mDisplayWindowSettings;
private final Context mContext;
private final Object mLock;
@@ -71,10 +74,6 @@
private StatusBarManagerInternal mStatusBarManagerInternal;
private SettingsObserver mSettingsObserver;
- // Default display does not rotate, apps that require non-default orientation will have to
- // have the orientation emulated.
- private boolean mForceDefaultOrientation;
-
private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@VisibleForTesting
@@ -93,6 +92,13 @@
private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
private int mUserRotation = Surface.ROTATION_0;
+ /**
+ * A flag to indicate if the display rotation should be fixed to user specified rotation
+ * regardless of all other states (including app requrested orientation). {@code true} the
+ * display rotation should be fixed to user specified rotation, {@code false} otherwise.
+ */
+ private boolean mFixedToUserRotation;
+
private int mDemoHdmiRotation;
private int mDemoRotation;
private boolean mDemoHdmiRotationLock;
@@ -100,15 +106,17 @@
DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
this(service, displayContent, displayContent.getDisplayPolicy(),
- service.mContext, service.getWindowManagerLock());
+ service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
}
@VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
- DisplayPolicy displayPolicy, Context context, Object lock) {
+ DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
+ Context context, Object lock) {
mService = service;
mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
+ mDisplayWindowSettings = displayWindowSettings;
mContext = context;
mLock = lock;
isDefaultDisplay = displayContent.isDefaultDisplay;
@@ -204,12 +212,19 @@
// so if the orientation is forced, we need to respect that no matter what.
final boolean isTv = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
- mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
- res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
- // For debug purposes the next line turns this feature off with:
- // $ adb shell setprop config.override_forced_orient true
- // $ adb shell wm size reset
- !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ final boolean forceDefaultOrientationInRes =
+ res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
+ final boolean forceDefaultOrienation =
+ ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
+ && forceDefaultOrientationInRes
+ // For debug purposes the next line turns this feature off with:
+ // $ adb shell setprop config.override_forced_orient true
+ // $ adb shell wm size reset
+ && !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ // Configuration says we force to use the default orientation. We can fall back to fix
+ // rotation to only user rotation. As long as OEM doesn't change user rotation then the
+ // rotation of this display is effectively stuck at 0 deg.
+ setFixedToUserRotation(forceDefaultOrienation);
}
void setRotation(int rotation) {
@@ -227,7 +242,14 @@
}
}
- void restoreUserRotation(int userRotationMode, int userRotation) {
+ void restoreSettings(int userRotationMode, int userRotation,
+ boolean fixedToUserRotation) {
+ mFixedToUserRotation = fixedToUserRotation;
+
+ // We will retrieve user rotation and user rotation mode from settings for default display.
+ if (isDefaultDisplay) {
+ return;
+ }
if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
&& userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
@@ -243,6 +265,18 @@
mUserRotation = userRotation;
}
+ void setFixedToUserRotation(boolean fixedToUserRotation) {
+ if (mFixedToUserRotation == fixedToUserRotation) {
+ return;
+ }
+
+ mFixedToUserRotation = fixedToUserRotation;
+ mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent,
+ fixedToUserRotation);
+ mService.updateRotation(true /* alwaysSendConfiguration */,
+ false /* forceRelayout */);
+ }
+
private void setUserRotation(int userRotationMode, int userRotation) {
if (isDefaultDisplay) {
// We'll be notified via settings listener, so we don't need to update internal values.
@@ -265,7 +299,7 @@
mUserRotation = userRotation;
changed = true;
}
- mService.mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
+ mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
userRotation);
if (changed) {
mService.updateRotation(true /* alwaysSendConfiguration */,
@@ -291,9 +325,8 @@
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
}
- /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
- boolean isDefaultOrientationForced() {
- return mForceDefaultOrientation;
+ boolean isFixedToUserRotation() {
+ return mFixedToUserRotation;
}
public int getLandscapeRotation() {
@@ -399,6 +432,12 @@
* screen is switched off.
*/
private boolean needSensorRunning() {
+ if (mFixedToUserRotation) {
+ // We are sure we only respect user rotation settings, so we are sure we will not
+ // support sensor rotation.
+ return false;
+ }
+
if (mSupportAutoRotation) {
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
@@ -459,8 +498,8 @@
);
}
- if (mForceDefaultOrientation) {
- return Surface.ROTATION_0;
+ if (mFixedToUserRotation) {
+ return mUserRotation;
}
int sensorRotation = mOrientationListener != null
@@ -701,8 +740,8 @@
// demo, hdmi, vr, etc mode.
// Determine if the rotation is currently forced.
- if (mForceDefaultOrientation) {
- return false; // Rotation is forced to default orientation.
+ if (mFixedToUserRotation) {
+ return false; // Rotation is forced to user settings.
}
final int lidState = mDisplayPolicy.getLidState();
@@ -861,6 +900,7 @@
pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+ pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation);
}
private class OrientationListener extends WindowOrientationListener {
@@ -945,4 +985,10 @@
}
}
}
+
+ @VisibleForTesting
+ interface ContentObserverRegister {
+ void registerContentObserver(Uri uri, boolean notifyForDescendants,
+ ContentObserver observer, @UserIdInt int userHandle);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 864f7e1..c7eadf9 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -87,10 +87,10 @@
}
@Override
- public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
synchronized (mGlobalLock) {
if (mContainer != null) {
- mContainer.mService.setNewDisplayOverrideConfiguration(overrideConfiguration,
+ mContainer.mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration,
mContainer);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index f7dfd3f..45d77de 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -80,6 +80,7 @@
private boolean mShouldShowWithInsecureKeyguard = false;
private boolean mShouldShowSystemDecors = false;
private boolean mShouldShowIme = false;
+ private boolean mFixedToUserRotation;
private Entry(String name) {
mName = name;
@@ -97,7 +98,8 @@
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
&& !mShouldShowWithInsecureKeyguard
&& !mShouldShowSystemDecors
- && !mShouldShowIme;
+ && !mShouldShowIme
+ && !mFixedToUserRotation;
}
}
@@ -186,6 +188,13 @@
writeSettingsIfNeeded(entry, displayInfo);
}
+ void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) {
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final Entry entry = getOrCreateEntry(displayInfo);
+ entry.mFixedToUserRotation = fixedToUserRotation;
+ writeSettingsIfNeeded(entry, displayInfo);
+ }
+
private int getWindowingModeLocked(Entry entry, int displayId) {
int windowingMode = entry != null ? entry.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -331,7 +340,8 @@
displayInfo.overscanRight = entry.mOverscanRight;
displayInfo.overscanBottom = entry.mOverscanBottom;
- dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation);
+ dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
+ entry.mUserRotation, entry.mFixedToUserRotation);
if (entry.mForcedDensity != 0) {
dc.mBaseDisplayDensity = entry.mForcedDensity;
@@ -458,6 +468,8 @@
"shouldShowWithInsecureKeyguard");
entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
+ entry.mFixedToUserRotation = getBooleanAttribute(parser,
+ "fixedToUserRotation");
mEntries.put(name, entry);
}
XmlUtils.skipCurrentTag(parser);
@@ -541,6 +553,10 @@
if (entry.mShouldShowIme) {
out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
}
+ if (entry.mFixedToUserRotation) {
+ out.attribute(null, "fixedToUserRotation",
+ Boolean.toString(entry.mFixedToUserRotation));
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 7ed078a..a667d67 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -31,13 +31,12 @@
import android.view.Display;
import android.view.IWindow;
import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
import com.android.internal.util.Preconditions;
-import android.view.InputWindowHandle;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
+
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -71,11 +70,18 @@
new IDragDropCallback() {});
boolean dragDropActiveLocked() {
- return mDragState != null;
+ return mDragState != null && !mDragState.isClosing();
}
- InputWindowHandle getInputWindowHandleLocked() {
- return mDragState.getInputWindowHandle();
+ void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+ mDragState.showInputSurface(t, displayId);
+ }
+
+ void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (mDragState != null) {
+ // TODO: Are we guaranteed to get here?
+ mDragState.hideInputSurface(t, displayId);
+ }
}
void registerCallback(IDragDropCallback callback) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a379266..607ee76 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -31,23 +31,24 @@
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
-import android.content.Context;
import android.graphics.Point;
+import android.graphics.Rect;
import android.hardware.input.InputManager;
+import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.IUserManager;
import android.os.UserManagerInternal;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
+import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.InputWindowHandle;
import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.View;
@@ -57,8 +58,6 @@
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
-import android.view.InputApplicationHandle;
-import android.view.InputWindowHandle;
import java.util.ArrayList;
@@ -118,6 +117,17 @@
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
private Point mDisplaySize = new Point();
+ // A surface used to catch input events for the drag-and-drop operation.
+ SurfaceControl mInputSurface;
+
+ private final Rect mTmpClipRect = new Rect();
+
+ /**
+ * Whether we are finishing this drag and drop. This starts with {@code false}, and is set to
+ * {@code true} when {@link #closeLocked()} is called.
+ */
+ private boolean mIsClosing;
+
DragState(WindowManagerService service, DragDropController controller, IBinder token,
SurfaceControl surface, int flags, IBinder localWin) {
mService = service;
@@ -127,6 +137,46 @@
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
+
+ }
+
+ boolean isClosing() {
+ return mIsClosing;
+ }
+
+ void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (displayId != mDisplayContent.getDisplayId()) {
+ return;
+ }
+
+ if (mInputSurface != null) {
+ t.hide(mInputSurface);
+ }
+ }
+
+ void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (displayId != mDisplayContent.getDisplayId()) {
+ return;
+ }
+
+ if (mInputSurface == null) {
+ mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+ .getSession()).setContainerLayer(true)
+ .setName("Drag and Drop Input Consumer").build();
+ }
+ final InputWindowHandle h = getInputWindowHandle();
+ if (h == null) {
+ Slog.w(TAG_WM, "Drag is in progress but there is no "
+ + "drag window handle.");
+ return;
+ }
+
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, h);
+ t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+ mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
}
/**
@@ -134,6 +184,7 @@
* DragDropController#mDragState becomes null.
*/
void closeLocked() {
+ mIsClosing = true;
// Unregister the input interceptor.
if (mInputInterceptor != null) {
if (DEBUG_DRAG)
@@ -218,7 +269,7 @@
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
- mDragApplicationHandle = new InputApplicationHandle(null);
+ mDragApplicationHandle = new InputApplicationHandle(new Binder());
mDragApplicationHandle.name = "drag";
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -226,7 +277,7 @@
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
display.getDisplayId());
mDragWindowHandle.name = "drag";
- mDragWindowHandle.inputChannel = mServerChannel;
+ mDragWindowHandle.token = mServerChannel.getToken();
mDragWindowHandle.layer = getDragLayerLocked();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index fddf6ca..7cb4a43 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,7 +31,6 @@
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class EmulatorDisplayOverlay {
private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM;
@@ -59,7 +57,7 @@
try {
ctrl = dc.makeOverlay()
.setName("EmulatorDisplayOverlay")
- .setSize(mScreenSize.x, mScreenSize.y)
+ .setBufferSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayer(zOrder);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 49bedc9..4df5a0b 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -25,6 +27,8 @@
import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+import android.util.Slog;
import java.io.PrintWriter;
@@ -39,6 +43,9 @@
final int mClientPid;
final UserHandle mClientUser;
+ final SurfaceControl mInputSurface;
+ Rect mTmpClipRect = new Rect();
+
InputConsumerImpl(WindowManagerService service, IBinder token, String name,
InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
mService = service;
@@ -58,14 +65,14 @@
}
mService.mInputManager.registerInputChannel(mServerChannel, null);
- mApplicationHandle = new InputApplicationHandle(null);
+ mApplicationHandle = new InputApplicationHandle(new Binder());
mApplicationHandle.name = name;
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
mWindowHandle.name = name;
- mWindowHandle.inputChannel = mServerChannel;
+ mWindowHandle.token = mServerChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
mWindowHandle.layoutParamsFlags = 0;
@@ -80,6 +87,10 @@
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
+
+ mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+ .getSession()).setContainerLayer(true).setName("Input Consumer " + name)
+ .build();
}
void linkToDeathRecipient() {
@@ -102,12 +113,33 @@
mToken.unlinkToDeath(this, 0);
}
- void layout(int dw, int dh) {
- mWindowHandle.touchableRegion.set(0, 0, dw, dh);
- mWindowHandle.frameLeft = 0;
- mWindowHandle.frameTop = 0;
- mWindowHandle.frameRight = dw;
- mWindowHandle.frameBottom = dh;
+ void layout(SurfaceControl.Transaction t, int dw, int dh) {
+ t.setPosition(mInputSurface, 0, 0);
+
+ mTmpClipRect.set(0, 0, dw, dh);
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
+ }
+
+ void layout(SurfaceControl.Transaction t, Rect r) {
+ t.setPosition(mInputSurface, r.left, r.top);
+ mTmpClipRect.set(0, 0, r.width(), r.height());
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
+ }
+
+ void hide(SurfaceControl.Transaction t) {
+ t.hide(mInputSurface);
+ }
+
+ void show(SurfaceControl.Transaction t, WindowState w) {
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, mWindowHandle);
+ t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+ }
+
+ void show(SurfaceControl.Transaction t, int layer) {
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, mWindowHandle);
+ t.setLayer(mInputSurface, layer);
}
private int getLayerLw(int windowType) {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 92ea1a9..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -6,21 +6,16 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.ActivityManager;
import android.os.Debug;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Slog;
+import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
-import android.view.InputApplicationHandle;
import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
-import android.view.InputChannel;
import java.io.PrintWriter;
-import java.util.HashMap;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
private final WindowManagerService mService;
@@ -72,8 +67,7 @@
* Called by the InputManager.
*/
@Override
- public long notifyANR(InputApplicationHandle inputApplicationHandle,
- IBinder token, String reason) {
+ public long notifyANR(IBinder token, String reason) {
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
@@ -84,9 +78,6 @@
appWindowToken = windowState.mAppToken;
}
}
- if (appWindowToken == null && inputApplicationHandle != null) {
- appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
- }
if (windowState != null) {
Slog.i(TAG_WM, "Input event dispatching timed out "
@@ -116,9 +107,7 @@
if (appWindowToken != null && appWindowToken.appToken != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- final AppWindowContainerController controller = appWindowToken.getController();
- final boolean abort = controller != null
- && controller.keyDispatchingTimedOut(reason,
+ final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
(windowState != null) ? windowState.mSession.mPid : -1);
if (!abort) {
// The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 83d32c8ad..c1e9a73 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -44,6 +44,7 @@
import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
import com.android.server.policy.WindowManagerPolicy;
@@ -61,9 +62,8 @@
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
- // Array of window handles to provide to the input dispatcher.
- private InputWindowHandle[] mInputWindowHandles;
- private int mInputWindowHandleCount;
+ // Currently focused input window handle.
+ private InputWindowHandle mFocusedInputWindowHandle;
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
@@ -72,6 +72,8 @@
private int mDisplayId;
+ SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction();
+
/**
* The set of input consumer added to the window manager by name, which consumes input events
* for the windows below it.
@@ -126,6 +128,7 @@
private boolean disposeInputConsumer(InputConsumerImpl consumer) {
if (consumer != null) {
consumer.disposeChannelsLw();
+ consumer.hide(mInputTransaction);
return true;
}
return false;
@@ -137,7 +140,16 @@
void layoutInputConsumers(int dw, int dh) {
for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
- mInputConsumers.valueAt(i).layout(dw, dh);
+ mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+ }
+ }
+
+ // The visibility of the input consumers is recomputed each time we
+ // update the input windows. We use a model where consumers begin invisible
+ // (set so by this function) and must meet some condition for visibility on each update.
+ void resetInputConsumers(SurfaceControl.Transaction t) {
+ for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+ mInputConsumers.valueAt(i).hide(t);
}
}
@@ -177,23 +189,12 @@
}
- private void addInputWindowHandle(final InputWindowHandle windowHandle) {
- if (mInputWindowHandles == null) {
- mInputWindowHandles = new InputWindowHandle[16];
- }
- if (mInputWindowHandleCount >= mInputWindowHandles.length) {
- mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
- mInputWindowHandleCount * 2);
- }
- mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
- }
-
- void addInputWindowHandle(final InputWindowHandle inputWindowHandle,
+ void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
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();
@@ -214,6 +215,11 @@
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
+ // Surface insets are hardcoded to be the same in all directions
+ // and we could probably deprecate the "left/right/top/bottom" concept.
+ // we avoid reintroducing this concept by just choosing one of them here.
+ inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
+
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
// to be inversely scaled to map from what is on screen
@@ -227,12 +233,9 @@
Slog.d(TAG_WM, "addInputWindowHandle: "
+ child + ", " + inputWindowHandle);
}
- addInputWindowHandle(inputWindowHandle);
- }
- private void clearInputWindowHandlesLw() {
- while (mInputWindowHandleCount != 0) {
- mInputWindowHandles[--mInputWindowHandleCount] = null;
+ if (hasFocus) {
+ mFocusedInputWindowHandle = inputWindowHandle;
}
}
@@ -261,14 +264,9 @@
if (DEBUG_DRAG) {
Log.d(TAG_WM, "Inserting drag window");
}
- final InputWindowHandle dragWindowHandle =
- mService.mDragDropController.getInputWindowHandleLocked();
- if (dragWindowHandle == null) {
- Slog.w(TAG_WM, "Drag is in progress but there is no "
- + "drag window handle.");
- } else if (dragWindowHandle.displayId == mDisplayId) {
- addInputWindowHandle(dragWindowHandle);
- }
+ mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
+ } else {
+ mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
}
final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
@@ -276,14 +274,9 @@
if (DEBUG_TASK_POSITIONING) {
Log.d(TAG_WM, "Inserting window handle for repositioning");
}
- final InputWindowHandle dragWindowHandle =
- mService.mTaskPositioningController.getDragWindowHandleLocked();
- if (dragWindowHandle == null) {
- Slog.e(TAG_WM,
- "Repositioning is in progress but there is no drag window handle.");
- } else if (dragWindowHandle.displayId == mDisplayId) {
- addInputWindowHandle(dragWindowHandle);
- }
+ mService.mTaskPositioningController.showInputSurface(mInputTransaction, mDisplayId);
+ } else {
+ mService.mTaskPositioningController.hideInputSurface(mInputTransaction, mDisplayId);
}
// Add all windows on the default display.
@@ -362,12 +355,6 @@
}
}
- void onRemoved() {
- // If DisplayContent removed, we need find a way to remove window handles of this display
- // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
- }
-
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
InputConsumerImpl navInputConsumer;
InputConsumerImpl pipInputConsumer;
@@ -401,16 +388,16 @@
final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
wallpaperController = dc.mWallpaperController;
- dc.forAllWindows(this, true /* traverseTopToBottom */);
+ resetInputConsumers(mInputTransaction);
+
+ dc.forAllWindows(this,
+ true /* traverseTopToBottom */);
+
if (mAddWallpaperInputConsumerHandle) {
- // No visible wallpaper found, add the wallpaper input consumer at the end.
- addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+ wallpaperInputConsumer.show(mInputTransaction, 0);
}
- // Send windows to native code.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
-
- clearInputWindowHandlesLw();
+ mInputTransaction.apply();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -420,7 +407,7 @@
final InputChannel inputChannel = w.mInputChannel;
final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || w.mRemoved
- || w.canReceiveTouchInput()) {
+ || w.cantReceiveTouchInput()) {
// Skip this window because it cannot possibly receive input.
return;
}
@@ -438,41 +425,36 @@
&& recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
if (recentsAnimationController.updateInputConsumerForApp(
recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
- addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+ recentsAnimationInputConsumer.show(mInputTransaction, w);
mAddRecentsAnimationInputConsumerHandle = false;
}
- // If the target app window does not yet exist, then we don't add the input
- // consumer window, but also don't add the app window below.
- return;
}
}
if (w.inPinnedWindowingMode()) {
- if (mAddPipInputConsumerHandle
- && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
+ if (mAddPipInputConsumerHandle) {
// Update the bounds of the Pip input consumer to match the window bounds.
w.getBounds(mTmpRect);
+ pipInputConsumer.layout(mInputTransaction, mTmpRect);
+
+ // The touchable region is relative to the surface top-left
+ mTmpRect.offsetTo(0, 0);
pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
- addInputWindowHandle(pipInputConsumer.mWindowHandle);
+ pipInputConsumer.show(mInputTransaction, w);
mAddPipInputConsumerHandle = false;
}
- // TODO: Fix w.canReceiveTouchInput() to handle this case
- if (!hasFocus) {
- // Skip this pinned stack window if it does not have focus
- return;
- }
}
if (mAddInputConsumerHandle
&& inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
- addInputWindowHandle(navInputConsumer.mWindowHandle);
+ navInputConsumer.show(mInputTransaction, w);
mAddInputConsumerHandle = false;
}
if (mAddWallpaperInputConsumerHandle) {
if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
// Add the wallpaper input consumer above the first visible wallpaper.
- addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+ wallpaperInputConsumer.show(mInputTransaction, w);
mAddWallpaperInputConsumerHandle = false;
}
}
@@ -490,8 +472,13 @@
mService.mDragDropController.sendDragStartedIfNeededLocked(w);
}
- addInputWindowHandle(
+ populateInputWindowHandle(
inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+
+ if (w.mWinAnimator.hasSurface()) {
+ mInputTransaction.setInputWindowInfo(
+ w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
new file mode 100644
index 0000000..282838f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
+
+import com.android.internal.util.function.TriConsumer;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import java.io.PrintWriter;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
+ */
+class InsetsSourceProvider {
+
+ private final Rect mTmpRect = new Rect();
+ private final @NonNull InsetsSource mSource;
+ private final DisplayContent mDisplayContent;
+ private final InsetsStateController mStateController;
+ private @Nullable InsetsSourceControl mControl;
+ private @Nullable WindowState mControllingWin;
+ private @Nullable ControlAdapter mAdapter;
+ private WindowState mWin;
+ private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+
+ InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
+ DisplayContent displayContent) {
+ mSource = source;
+ mDisplayContent = displayContent;
+ mStateController = stateController;
+ }
+
+ InsetsSource getSource() {
+ return mSource;
+ }
+
+ /**
+ * Updates the window that currently backs this source.
+ *
+ * @param win The window that links to this source.
+ * @param frameProvider Based on display frame state and the window, calculates the resulting
+ * frame that should be reported to clients.
+ */
+ void setWindow(@Nullable WindowState win,
+ @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+ if (mWin != null) {
+ mWin.setInsetProvider(null);
+ }
+ mWin = win;
+ mFrameProvider = frameProvider;
+ if (win == null) {
+ mSource.setVisible(false);
+ mSource.setFrame(new Rect());
+ } else {
+ mSource.setVisible(true);
+ mWin.setInsetProvider(this);
+ }
+ }
+
+ /**
+ * Called when a layout pass has occurred.
+ */
+ void onPostLayout() {
+ if (mWin == null) {
+ return;
+ }
+
+ mTmpRect.set(mWin.getFrameLw());
+ if (mFrameProvider != null) {
+ mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+ } else {
+ mTmpRect.inset(mWin.mGivenContentInsets);
+ }
+ mSource.setFrame(mTmpRect);
+ mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
+
+ }
+
+ void updateControlForTarget(@Nullable WindowState target) {
+ if (target == mControllingWin) {
+ return;
+ }
+ if (target == null) {
+ revokeControl();
+ return;
+ }
+ mAdapter = new ControlAdapter();
+ mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
+ false /* TODO hidden */);
+ mControllingWin = target;
+ mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
+ }
+
+ InsetsSourceControl getControl() {
+ return mControl;
+ }
+
+ void revokeControl() {
+ if (mControllingWin != null) {
+
+ // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
+ mWin.cancelAnimation();
+ }
+ }
+
+ private class ControlAdapter implements AnimationAdapter {
+
+ private SurfaceControl mCapturedLeash;
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+ mCapturedLeash = animationLeash;
+ t.setPosition(mCapturedLeash, mSource.getFrame().left, mSource.getFrame().top);
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (mAdapter == this) {
+ mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+ mControl = null;
+ mControllingWin = null;
+ mAdapter = null;
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 0;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream proto) {
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
new file mode 100644
index 0000000..592b7fb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.ViewRootImpl;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Manages global window inset state in the system represented by {@link InsetsState}.
+ */
+class InsetsStateController {
+
+ private final InsetsState mLastState = new InsetsState();
+ private final InsetsState mState = new InsetsState();
+ private final DisplayContent mDisplayContent;
+
+ private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
+ private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>();
+ private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>();
+ private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>();
+
+ private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
+ if (w.isVisible()) {
+ w.notifyInsetsChanged();
+ }
+ };
+
+ InsetsStateController(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ }
+
+ /**
+ * When dispatching window state to the client, we'll need to exclude the source that represents
+ * the window that is being dispatched.
+ *
+ * @param target The client we dispatch the state to.
+ * @return The state stripped of the necessary information.
+ */
+ InsetsState getInsetsForDispatch(WindowState target) {
+ final InsetsSourceProvider provider = target.getInsetProvider();
+ if (provider == null) {
+ return mState;
+ }
+
+ final InsetsState state = new InsetsState();
+ state.set(mState);
+ final int type = provider.getSource().getType();
+ state.removeSource(type);
+
+ // Navigation bar doesn't get influenced by anything else
+ if (type == TYPE_NAVIGATION_BAR) {
+ state.removeSource(TYPE_IME);
+ state.removeSource(TYPE_TOP_BAR);
+ }
+ return state;
+ }
+
+ @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
+ ArrayList<Integer> controlled = mWinControlTypeMap.get(target);
+ if (controlled == null) {
+ return null;
+ }
+ final int size = controlled.size();
+ final InsetsSourceControl[] result = new InsetsSourceControl[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = mControllers.get(controlled.get(i)).getControl();
+ }
+ return result;
+ }
+
+ /**
+ * @return The provider of a specific type.
+ */
+ InsetsSourceProvider getSourceProvider(int type) {
+ return mControllers.computeIfAbsent(type,
+ key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+ }
+
+ /**
+ * Called when a layout pass has occurred.
+ */
+ void onPostLayout() {
+ for (int i = mControllers.size() - 1; i>= 0; i--) {
+ mControllers.valueAt(i).onPostLayout();
+ }
+ if (!mLastState.equals(mState)) {
+ mLastState.set(mState, true /* copySources */);
+ notifyInsetsChanged();
+ }
+ }
+
+ void onImeTargetChanged(@Nullable WindowState imeTarget) {
+ onControlChanged(TYPE_IME, imeTarget);
+ notifyPendingInsetsControlChanged();
+ }
+
+ /**
+ * Called when the top opaque fullscreen window that is able to control the system bars changes.
+ *
+ * @param controllingWindow The window that is now able to control the system bars appearance
+ * and visibility.
+ */
+ void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
+ // TODO: Apply policy that determines whether controllingWindow is able to control system
+ // bars
+
+ // TODO: Depending on the form factor, mapping is different
+ onControlChanged(TYPE_TOP_BAR, controllingWindow);
+ onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
+ notifyPendingInsetsControlChanged();
+ }
+
+ void notifyControlRevoked(@NonNull WindowState previousControllingWin,
+ InsetsSourceProvider provider) {
+ removeFromControlMaps(previousControllingWin, provider.getSource().getType());
+ }
+
+ private void onControlChanged(int type, @Nullable WindowState win) {
+ if (!ViewRootImpl.USE_NEW_INSETS) {
+ return;
+ }
+ final WindowState previous = mTypeWinControlMap.get(type);
+ if (win == previous) {
+ return;
+ }
+ final InsetsSourceProvider controller = mControllers.get(type);
+ if (controller == null) {
+ return;
+ }
+ controller.updateControlForTarget(win);
+ if (previous != null) {
+ removeFromControlMaps(previous, type);
+ mPendingControlChanged.add(previous);
+ }
+ if (win != null) {
+ addToControlMaps(win, type);
+ mPendingControlChanged.add(win);
+ }
+ }
+
+ private void removeFromControlMaps(@NonNull WindowState win, int type) {
+ final ArrayList<Integer> array = mWinControlTypeMap.get(win);
+ if (array == null) {
+ return;
+ }
+ array.remove((Integer) type);
+ if (array.isEmpty()) {
+ mWinControlTypeMap.remove(win);
+ }
+ mTypeWinControlMap.remove(type);
+ }
+
+ private void addToControlMaps(@NonNull WindowState win, int type) {
+ final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win,
+ key -> new ArrayList<>());
+ array.add(type);
+ mTypeWinControlMap.put(type, win);
+ }
+
+ private void notifyPendingInsetsControlChanged() {
+ mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+ for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
+ final WindowState controllingWin = mPendingControlChanged.valueAt(i);
+ controllingWin.notifyInsetsControlChanged();
+ }
+ mPendingControlChanged.clear();
+ });
+ }
+
+ private void notifyInsetsChanged() {
+ mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "WindowInsetsStateController");
+ mState.dump(prefix + " ", pw);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c91af73..4ef3513 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -72,6 +72,7 @@
private int mVisibilityTransactionDepth;
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
+ private RootActivityContainer mRootActivityContainer;
KeyguardController(ActivityTaskManagerService service,
ActivityStackSupervisor stackSupervisor) {
@@ -81,6 +82,7 @@
void setWindowManager(WindowManagerService windowManager) {
mWindowManager = windowManager;
+ mRootActivityContainer = mService.mRootActivityContainer;
}
/**
@@ -146,7 +148,7 @@
mDismissalRequested = false;
}
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
updateKeyguardSleepToken();
}
@@ -172,16 +174,17 @@
mWindowManager.deferSurfaceLayout();
try {
setKeyguardGoingAway(true);
- mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
false /* alwaysKeepCurrent */, convertTransitFlags(flags),
false /* forceOverride */);
updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.addStartingWindowsForVisibleActivities(
+ true /* taskSwitch */);
mWindowManager.executeAppTransition();
} finally {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
@@ -277,8 +280,9 @@
private void visibilitiesUpdated() {
boolean requestDismissKeyguard = false;
- for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
- final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+ displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
final KeyguardDisplayState state = getDisplay(display.mDisplayId);
state.visibilitiesUpdated(this, display);
requestDismissKeyguard |= state.mRequestDismissKeyguard;
@@ -298,12 +302,12 @@
if (isKeyguardLocked()) {
mWindowManager.deferSurfaceLayout();
try {
- mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
.prepareAppTransition(resolveOccludeTransit(),
false /* alwaysKeepCurrent */, 0 /* flags */,
true /* forceOverride */);
updateKeyguardSleepToken();
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
} finally {
mWindowManager.continueSurfaceLayout();
@@ -319,21 +323,23 @@
// We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
// reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
// insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
- if (mWindowManager.isKeyguardSecure()) {
- mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- mDismissalRequested = true;
+ if (!mWindowManager.isKeyguardSecure()) {
+ return;
+ }
- // 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 =
- mStackSupervisor.getDefaultDisplay().getWindowContainerController();
- if (mKeyguardShowing && canDismissKeyguard()
- && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
- dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
- 0 /* flags */, true /* forceOverride */);
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mWindowManager.executeAppTransition();
- }
+ mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+ mDismissalRequested = true;
+
+ // 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();
+ if (mKeyguardShowing && canDismissKeyguard()
+ && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+ dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+ 0 /* flags */, true /* forceOverride */);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mWindowManager.executeAppTransition();
}
}
@@ -350,7 +356,7 @@
private int resolveOccludeTransit() {
final DisplayWindowController dwc =
- mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
if (mBeforeUnoccludeTransit != TRANSIT_UNSET
&& dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
// TODO(b/113840485): Handle app transition for individual display.
@@ -377,7 +383,8 @@
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack stack =
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
return;
}
@@ -387,8 +394,9 @@
}
private void updateKeyguardSleepToken() {
- for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
- final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+ displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
final KeyguardDisplayState state = getDisplay(display.mDisplayId);
if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
state.acquiredSleepToken();
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
new file mode 100644
index 0000000..93e2d8d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayList;
+
+/**
+ * Multi-cast implementation of {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br /><br />
+ * If this class is called through the {@link ActivityMetricsLaunchObserver} interface,
+ * then the call is forwarded to all registered observers at the time.
+ *
+ * <br /><br />
+ * All calls are invoked asynchronously in-order on a background thread. This fulfills the
+ * sequential ordering guarantee in {@link ActivityMetricsLaunchObserverRegistry}.
+ *
+ * @see ActivityTaskManagerInternal#getLaunchObserverRegistry()
+ */
+class LaunchObserverRegistryImpl implements
+ ActivityMetricsLaunchObserverRegistry, ActivityMetricsLaunchObserver {
+ private final ArrayList<ActivityMetricsLaunchObserver> mList = new ArrayList<>();
+
+ /**
+ * All calls are posted to a handler because:
+ *
+ * 1. We don't know how long the observer will take to handle this call and we don't want
+ * to block the WM critical section on it.
+ * 2. We don't know the lock ordering of the observer so we don't want to expose a chance
+ * of deadlock.
+ */
+ private final Handler mHandler;
+
+ public LaunchObserverRegistryImpl(Looper looper) {
+ mHandler = new Handler(looper);
+ }
+
+ @Override
+ public void registerLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleRegisterLaunchObserver, this, launchObserver));
+ }
+
+ @Override
+ public void unregisterLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleUnregisterLaunchObserver, this, launchObserver));
+ }
+
+ @Override
+ public void onIntentStarted(Intent intent) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+ }
+
+ @Override
+ public void onIntentFailed() {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnIntentFailed, this));
+ }
+
+ @Override
+ public void onActivityLaunched(
+ @ActivityRecordProto byte[] activity,
+ int temperature) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunched,
+ this, activity, temperature));
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(
+ @ActivityRecordProto byte[] activity) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, activity));
+ }
+
+ @Override
+ public void onActivityLaunchFinished(
+ @ActivityRecordProto byte[] activity) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+ }
+
+ // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
+ // unbound (i.e. not capture any variables explicitly or implicitly) to fulfill the
+ // singleton-lambda requirement.
+
+ private void handleRegisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+ mList.add(observer);
+ }
+
+ private void handleUnregisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+ mList.remove(observer);
+ }
+
+ private void handleOnIntentStarted(Intent intent) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onIntentStarted(intent);
+ }
+ }
+
+ private void handleOnIntentFailed() {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onIntentFailed();
+ }
+ }
+
+ private void handleOnActivityLaunched(
+ @ActivityRecordProto byte[] activity,
+ @Temperature int temperature) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunched(activity, temperature);
+ }
+ }
+
+ private void handleOnActivityLaunchCancelled(
+ @ActivityRecordProto byte[] activity) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchCancelled(activity);
+ }
+ }
+
+ private void handleOnActivityLaunchFinished(
+ @ActivityRecordProto byte[] activity) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchFinished(activity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 72d5143..3062d34 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -223,7 +223,8 @@
private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) {
final ActivityStack<?> stack = task.getStack();
final int displayId = stack.mDisplayId;
- final ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+ final ActivityDisplay display =
+ mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
final DisplayInfo info = new DisplayInfo();
display.mDisplay.getDisplayInfo(info);
@@ -245,8 +246,8 @@
}
void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams outParams) {
- final ComponentName name = task != null ? task.realActivity : activity.realActivity;
- final int userId = task != null ? task.userId : activity.userId;
+ final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
+ final int userId = task != null ? task.userId : activity.mUserId;
outParams.reset();
Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
@@ -259,7 +260,7 @@
return;
}
- final ActivityDisplay display = mSupervisor.getActivityDisplay(
+ final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(
persistableParams.mDisplayUniqueId);
if (display != null) {
outParams.mPreferredDisplayId = display.mDisplayId;
@@ -268,7 +269,7 @@
outParams.mBounds.set(persistableParams.mBounds);
}
- private void onPackageRemoved(String packageName) {
+ void removeRecordForPackage(String packageName) {
final List<File> fileToDelete = new ArrayList<>();
for (int i = 0; i < mMap.size(); ++i) {
int userId = mMap.keyAt(i);
@@ -309,7 +310,7 @@
@Override
public void onPackageRemoved(String packageName) {
- LaunchParamsPersister.this.onPackageRemoved(packageName);
+ removeRecordForPackage(packageName);
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index b49d304..1a2aa2f 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -186,7 +186,6 @@
createSurface();
}
t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
- t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
t.setWindowCrop(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
t.show(mSurface);
} else if (mSurface != null) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 41d0777..d2f2863 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -225,7 +225,7 @@
* of the last locked task and finishing it would mean that lock task mode is ended illegally.
*/
boolean activityBlockedFromFinish(ActivityRecord activity) {
- final TaskRecord task = activity.getTask();
+ final TaskRecord task = activity.getTaskRecord();
if (activity == task.getRootActivity()
&& activity == task.getTopActivity()
&& task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
@@ -447,7 +447,7 @@
return;
}
task.performClearTaskLocked();
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
/**
@@ -579,7 +579,7 @@
if (andResume) {
mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
final ActivityStack stack = task.getStack();
if (stack != null) {
stack.getDisplay().getWindowContainerController().executeAppTransition();
@@ -641,12 +641,13 @@
taskChanged = true;
}
- for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
+ for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1;
+ displayNdx >= 0; --displayNdx) {
+ mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated();
}
- final ActivityRecord r = mSupervisor.topRunningActivityLocked();
- final TaskRecord task = (r != null) ? r.getTask() : null;
+ final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity();
+ final TaskRecord task = (r != null) ? r.getTaskRecord() : null;
if (mLockTaskModeTasks.isEmpty() && task!= null
&& task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
// This task must have just been authorized.
@@ -657,7 +658,7 @@
}
if (taskChanged) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
index 3ef42e7..1c7ebd6 100644
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -41,7 +41,7 @@
PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
Rect outBounds) {
return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
- mStackSupervisor.mWindowManager);
+ mRootActivityContainer.mWindowManager);
}
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 476c1f9..9422182 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;
@@ -79,7 +79,7 @@
int callingPid) {
mService = atm;
mStackSupervisor = stackSupervisor;
- mDefaultDisplay = stackSupervisor.getDefaultDisplay();
+ mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay();
mActivityStartController = activityStartController;
mWindowManager = wm;
mCallingPid = callingPid;
@@ -94,7 +94,7 @@
// TODO(multi-display) currently only support recents animation in default display.
final DisplayWindowController dwc =
- mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+ mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
if (!mWindowManager.canStartRecentsAnimation()) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
@@ -124,8 +124,8 @@
// Send launch hint if we are actually launching the target. If it's already visible
// (shouldn't happen in general) we don't need to send it.
if (targetActivity == null || !targetActivity.visible) {
- mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
- targetActivity);
+ mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ true /* forceSend */, targetActivity);
}
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
@@ -148,8 +148,8 @@
// If there are multiple tasks in the target stack (ie. the home stack, with 3p
// and default launchers coexisting), then move the task to the top as a part of
// moving the stack to the front
- if (targetStack.topTask() != targetActivity.getTask()) {
- targetStack.addTask(targetActivity.getTask(), true /* toTop */,
+ if (targetStack.topTask() != targetActivity.getTaskRecord()) {
+ targetStack.addTask(targetActivity.getTaskRecord(), true /* toTop */,
"startRecentsActivity");
}
} else {
@@ -192,7 +192,7 @@
// If we updated the launch-behind state, update the visibility of the activities after
// we fetch the visible tasks to be controlled by the animation
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
targetActivity);
@@ -215,7 +215,8 @@
@Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
final AppOpsManager appOpsManager = (AppOpsManager)
mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
- final List<IBinder> topActivities = mStackSupervisor.getTopVisibleActivities();
+ final List<IBinder> topActivities =
+ mService.mRootActivityContainer.getTopVisibleActivities();
final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks;
if (assistDataReceiver != null) {
assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver,
@@ -224,8 +225,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)) {
@@ -235,8 +236,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;
@@ -283,7 +284,7 @@
// Just to be sure end the launch hint in case the target activity was never launched.
// However, if we're keeping the activity and making it visible, we can leave it on.
if (reorderMode != REORDER_KEEP_IN_PLACE) {
- mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
+ mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
}
mService.mH.post(
@@ -343,8 +344,8 @@
}
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
// No reason to wait for the pausing activity in this case, as the hiding of
// surfaces needs to be done immediately.
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
new file mode 100644
index 0000000..f7877c0
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -0,0 +1,2297 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+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.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+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.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Build;
+import android.os.FactoryTest;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.UserState;
+
+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.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Root node for activity containers.
+ * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The
+ * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy.
+ */
+class RootActivityContainer extends ConfigurationContainer
+ implements DisplayManager.DisplayListener {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RootActivityContainer" : TAG_ATM;
+ static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+ private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+ static final String TAG_STATES = TAG + POSTFIX_STATES;
+ private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+
+ /**
+ * The modes which affect which tasks are returned when calling
+ * {@link RootActivityContainer#anyTaskForId(int)}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ MATCH_TASK_IN_STACKS_ONLY,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ })
+ public @interface AnyTaskForIdMatchTaskMode {}
+ // Match only tasks in the current stacks
+ static final int MATCH_TASK_IN_STACKS_ONLY = 0;
+ // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
+ static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
+ // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
+ // provided stack id
+ static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+
+ ActivityTaskManagerService mService;
+ ActivityStackSupervisor mStackSupervisor;
+ WindowManagerService mWindowManager;
+ DisplayManager mDisplayManager;
+ private DisplayManagerInternal mDisplayManagerInternal;
+ // TODO: Remove after object merge with RootWindowContainer.
+ private RootWindowContainer mRootWindowContainer;
+
+ /**
+ * List of displays which contain activities, sorted by z-order.
+ * The last entry in the list is the topmost.
+ */
+ private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
+
+ /** Reference to default display so we can quickly look it up. */
+ private ActivityDisplay mDefaultDisplay;
+ private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+ /** The current user */
+ int mCurrentUser;
+ /** Stack id of the front stack when user switched, indexed by userId. */
+ SparseIntArray mUserStackInFront = new SparseIntArray(2);
+
+ /**
+ * A list of tokens that cause the top activity to be put to sleep.
+ * They are used by components that may hide and block interaction with underlying
+ * activities.
+ */
+ final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
+
+ /** Is dock currently minimized. */
+ boolean mIsDockMinimized;
+
+ /** Set when a power hint has started, but not ended. */
+ private boolean mPowerHintSent;
+
+ // The default minimal size that will be used if the activity doesn't specify its minimal size.
+ // It will be calculated when the default display gets added.
+ int mDefaultMinSizeOfResizeableTaskDp = -1;
+
+ // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+ private boolean mTaskLayersChanged = true;
+
+ private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
+ private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+ static class FindTaskResult {
+ ActivityRecord mRecord;
+ boolean mIdealMatch;
+
+ void clear() {
+ mRecord = null;
+ mIdealMatch = false;
+ }
+
+ void setTo(FindTaskResult result) {
+ mRecord = result.mRecord;
+ mIdealMatch = result.mIdealMatch;
+ }
+ }
+
+ RootActivityContainer(ActivityTaskManagerService service) {
+ mService = service;
+ mStackSupervisor = service.mStackSupervisor;
+ mStackSupervisor.mRootActivityContainer = this;
+ }
+
+ @VisibleForTesting
+ void setWindowContainer(RootWindowContainer container) {
+ mRootWindowContainer = container;
+ mRootWindowContainer.setRootActivityContainer(this);
+ }
+
+ void setWindowManager(WindowManagerService wm) {
+ mWindowManager = wm;
+ setWindowContainer(mWindowManager.mRoot);
+ mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(this, mService.mH);
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
+ final Display[] displays = mDisplayManager.getDisplays();
+ for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
+ final Display display = displays[displayNdx];
+ final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+ if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
+ mDefaultDisplay = activityDisplay;
+ }
+ addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
+ }
+ calculateDefaultMinimalSizeOfResizeableTasks();
+
+ final ActivityDisplay defaultDisplay = getDefaultDisplay();
+
+ defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
+ }
+
+ // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+ ActivityDisplay getDefaultDisplay() {
+ return mDefaultDisplay;
+ }
+
+ /**
+ * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
+ * defined in {@link DisplayInfo#uniqueId}.
+ *
+ * @param uniqueId the unique ID of the display
+ * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
+ */
+ ActivityDisplay getActivityDisplay(String uniqueId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ final boolean isValid = display.mDisplay.isValid();
+ if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
+ return display;
+ }
+ }
+
+ return null;
+ }
+
+ // TODO: Look into consolidating with getActivityDisplayOrCreate()
+ ActivityDisplay getActivityDisplay(int displayId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+ if (activityDisplay.mDisplayId == displayId) {
+ return activityDisplay;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get an existing instance of {@link ActivityDisplay} or create new if there is a
+ * corresponding record in display manager.
+ */
+ // TODO: Look into consolidating with getActivityDisplay()
+ ActivityDisplay getActivityDisplayOrCreate(int displayId) {
+ ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+ if (activityDisplay != null) {
+ return activityDisplay;
+ }
+ if (mDisplayManager == null) {
+ // The system isn't fully initialized yet.
+ return null;
+ }
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ // The display is not registered in DisplayManager.
+ return null;
+ }
+ // The display hasn't been added to ActivityManager yet, create a new record now.
+ activityDisplay = new ActivityDisplay(this, display);
+ addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
+ return activityDisplay;
+ }
+
+ /** Check if display with specified id is added to the list. */
+ boolean isDisplayAdded(int displayId) {
+ return getActivityDisplayOrCreate(displayId) != null;
+ }
+
+ ActivityRecord getDefaultDisplayHomeActivity() {
+ return getDefaultDisplayHomeActivityForUser(mCurrentUser);
+ }
+
+ ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+ return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
+ }
+
+ boolean startHomeOnAllDisplays(int userId, String reason) {
+ boolean homeStarted = false;
+ for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+ final int displayId = mActivityDisplays.get(i).mDisplayId;
+ homeStarted |= startHomeOnDisplay(userId, reason, displayId);
+ }
+ return homeStarted;
+ }
+
+ /**
+ * This starts home activity on displays that can have system decorations and only if the
+ * home activity can have multiple instances.
+ */
+ boolean startHomeOnDisplay(int userId, String reason, int displayId) {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+ if (aInfo == null) {
+ return false;
+ }
+
+ if (!canStartHomeOnDisplay(aInfo, displayId,
+ false /* allowInstrumenting */)) {
+ return false;
+ }
+
+ // Update the reason for ANR debugging to verify if the user activity is the one that
+ // actually launched.
+ final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
+ aInfo.applicationInfo.uid);
+ mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
+ displayId);
+ return true;
+ }
+
+ /**
+ * This resolves the home activity info and updates the home component of the given intent.
+ * @return the home activity info if any.
+ */
+ private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+ final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+ final ComponentName comp = homeIntent.getComponent();
+ ActivityInfo aInfo = null;
+ try {
+ if (comp != null) {
+ // Factory test.
+ aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+ } else {
+ final String resolvedType =
+ homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+ final ResolveInfo info = AppGlobals.getPackageManager()
+ .resolveIntent(homeIntent, resolvedType, flags, userId);
+ if (info != null) {
+ aInfo = info.activityInfo;
+ }
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+
+ if (aInfo == null) {
+ Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
+ return null;
+ }
+
+ homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+ aInfo = new ActivityInfo(aInfo);
+ aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+ homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
+ return aInfo;
+ }
+
+ boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
+ if (!mService.isBooting() && !mService.isBooted()) {
+ // Not ready yet!
+ return false;
+ }
+
+ if (displayId == INVALID_DISPLAY) {
+ displayId = DEFAULT_DISPLAY;
+ }
+
+ final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+ final String myReason = reason + " resumeHomeActivity";
+
+ // Only resume home activity if isn't finishing.
+ if (r != null && !r.finishing) {
+ r.moveFocusableActivityToTop(myReason);
+ return resumeFocusedStacksTopActivities(r.getActivityStack(), prev, null);
+ }
+ return startHomeOnDisplay(mCurrentUser, myReason, displayId);
+ }
+
+ /**
+ * Check if home activity start should be allowed on a display.
+ * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
+ * @param displayId The id of the target display.
+ * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
+ * @return {@code true} if allow to launch, {@code false} otherwise.
+ */
+ boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
+ boolean allowInstrumenting) {
+ if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
+ && mService.mTopAction == null) {
+ // We are running in factory test mode, but unable to find the factory test app, so
+ // just sit around displaying the error message and don't try to start anything.
+ return false;
+ }
+
+ final WindowProcessController app =
+ mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
+ if (!allowInstrumenting && app != null && app.isInstrumenting()) {
+ // Don't do this if the home app is currently being instrumented.
+ return false;
+ }
+
+ if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+ && displayId == mService.mVr2dDisplayId)) {
+ // No restrictions to default display or vr 2d display.
+ return true;
+ }
+
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+ // Can't launch home on display that doesn't support system decorations.
+ return false;
+ }
+
+ final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
+ && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
+ && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+ if (!supportMultipleInstance) {
+ // Can't launch home on other displays if it requested to be single instance. Also we
+ // don't allow home applications that target before Q to have multiple home activity
+ // instances because they may not be expected to have multiple home scenario and
+ // haven't explicitly request for single instance.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Ensure all activities visibility, update orientation and configuration.
+ *
+ * @param starting The currently starting activity or {@code null} if there is none.
+ * @param displayId The id of the display where operation is executed.
+ * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
+ * {@code true} if config changed.
+ * @param deferResume Whether to defer resume while updating config.
+ * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+ * because of configuration update.
+ */
+ boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
+ boolean markFrozenIfConfigChanged, boolean deferResume) {
+ // First ensure visibility without updating the config just yet. We need this to know what
+ // activities are affecting configuration now.
+ // Passing null here for 'starting' param value, so that visibility of actual starting
+ // activity will be properly updated.
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */, false /* notifyClients */);
+
+ if (displayId == INVALID_DISPLAY) {
+ // The caller didn't provide a valid display id, skip updating config.
+ return true;
+ }
+
+ // Force-update the orientation from the WindowManager, since we need the true configuration
+ // to send to the client now.
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ getDisplayOverrideConfiguration(displayId),
+ starting != null && starting.mayFreezeScreenLocked(starting.app)
+ ? starting.appToken : null,
+ displayId, true /* forceUpdate */);
+ if (starting != null && markFrozenIfConfigChanged && config != null) {
+ starting.frozenBeforeDestroy = true;
+ }
+
+ // Update the configuration of the activities on the display.
+ return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+ displayId);
+ }
+
+ /**
+ * @return a list of activities which are the top ones in each visible stack. The first
+ * entry will be the focused activity.
+ */
+ List<IBinder> getTopVisibleActivities() {
+ final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ // Traverse all displays.
+ for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ // Traverse all stacks on a display.
+ for (int j = display.getChildCount() - 1; j >= 0; --j) {
+ final ActivityStack stack = display.getChildAt(j);
+ // Get top activity from a visible stack and add it to the list.
+ if (stack.shouldBeVisible(null /* starting */)) {
+ final ActivityRecord top = stack.getTopActivity();
+ if (top != null) {
+ if (stack == topFocusedStack) {
+ topActivityTokens.add(0, top.appToken);
+ } else {
+ topActivityTokens.add(top.appToken);
+ }
+ }
+ }
+ }
+ }
+ return topActivityTokens;
+ }
+
+ ActivityStack getTopDisplayFocusedStack() {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
+ if (focusedStack != null) {
+ return focusedStack;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord getTopResumedActivity() {
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity != null && resumedActivity.app != null) {
+ return resumedActivity;
+ }
+ // The top focused stack might not have a resumed activity yet - look on all displays in
+ // focus order.
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+ if (resumedActivityOnDisplay != null) {
+ return resumedActivityOnDisplay;
+ }
+ }
+ return null;
+ }
+
+ boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+ if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+ return false;
+ }
+
+ return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+ }
+
+ boolean isTopDisplayFocusedStack(ActivityStack stack) {
+ return stack != null && stack == getTopDisplayFocusedStack();
+ }
+
+ void updatePreviousProcess(ActivityRecord r) {
+ // Now that this process has stopped, we may want to consider it to be the previous app to
+ // try to keep around in case the user wants to return to it.
+
+ // First, found out what is currently the foreground app, so that we don't blow away the
+ // previous app if this activity is being hosted by the process that is actually still the
+ // foreground.
+ WindowProcessController fgApp = null;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (isTopDisplayFocusedStack(stack)) {
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity != null) {
+ fgApp = resumedActivity.app;
+ } else if (stack.mPausingActivity != null) {
+ fgApp = stack.mPausingActivity.app;
+ }
+ break;
+ }
+ }
+ }
+
+ // Now set this one as the previous process, only if that really makes sense to.
+ if (r.hasProcess() && fgApp != null && r.app != fgApp
+ && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+ && r.app != mService.mHomeProcess) {
+ mService.mPreviousProcess = r.app;
+ mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+ }
+ }
+
+ boolean attachApplication(WindowProcessController app) throws RemoteException {
+ final String processName = app.mName;
+ boolean didSomething = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final ActivityStack stack = display.getFocusedStack();
+ if (stack != null) {
+ stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
+ final ActivityRecord top = stack.topRunningActivityLocked();
+ final int size = mTmpActivityList.size();
+ for (int i = 0; i < size; i++) {
+ final ActivityRecord activity = mTmpActivityList.get(i);
+ if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
+ && processName.equals(activity.processName)) {
+ try {
+ if (mStackSupervisor.realStartActivityLocked(activity, app,
+ top == activity /* andResume */, true /* checkConfig */)) {
+ didSomething = true;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception in new application when starting activity "
+ + top.intent.getComponent().flattenToShortString(), e);
+ throw e;
+ }
+ }
+ }
+ }
+ }
+ if (!didSomething) {
+ ensureActivitiesVisible(null, 0, false /* preserve_windows */);
+ }
+ return didSomething;
+ }
+
+ /**
+ * Make sure that all activities that need to be visible in the system actually are and update
+ * their configuration.
+ */
+ void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+ boolean preserveWindows) {
+ ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+ }
+
+ /**
+ * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+ */
+ void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+ boolean preserveWindows, boolean notifyClients) {
+ mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+ try {
+ // First the front stacks. In case any are not fullscreen and are in front of home.
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+ notifyClients);
+ }
+ }
+ } finally {
+ mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+ }
+ }
+
+ boolean switchUser(int userId, UserState uss) {
+ final int focusStackId = getTopDisplayFocusedStack().getStackId();
+ // We dismiss the docked stack whenever we switch users.
+ final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+ if (dockedStack != null) {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(
+ dockedStack, dockedStack.isFocusedStackOnDisplay());
+ }
+ // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
+ // also cause all tasks to be moved to the fullscreen stack at a position that is
+ // appropriate.
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+ mUserStackInFront.put(mCurrentUser, focusStackId);
+ final int restoreStackId =
+ mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
+ mCurrentUser = userId;
+
+ mStackSupervisor.mStartingUsers.add(uss);
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.switchUserLocked(userId);
+ TaskRecord task = stack.topTask();
+ if (task != null) {
+ stack.positionChildWindowContainerAtTop(task);
+ }
+ }
+ }
+
+ ActivityStack stack = getStack(restoreStackId);
+ if (stack == null) {
+ stack = getDefaultDisplay().getHomeStack();
+ }
+ final boolean homeInFront = stack.isActivityTypeHome();
+ if (stack.isOnHomeDisplay()) {
+ stack.moveToFront("switchUserOnHomeDisplay");
+ } else {
+ // Stack was moved to another display while user was swapped out.
+ resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
+ }
+ return homeInFront;
+ }
+
+ void removeUser(int userId) {
+ mUserStackInFront.delete(userId);
+ }
+
+ /**
+ * Update the last used stack id for non-current user (current user's last
+ * used stack is the focused stack)
+ */
+ void updateUserStack(int userId, ActivityStack stack) {
+ if (userId != mCurrentUser) {
+ mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+ : getDefaultDisplay().getHomeStack().mStackId);
+ }
+ }
+
+ void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+ Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+ boolean deferResume) {
+
+ if (stack.inSplitScreenPrimaryWindowingMode()) {
+ mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds,
+ tempTaskInsetBounds, null, null, preserveWindows, deferResume);
+ return;
+ }
+
+ final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
+ if (!allowResizeInDockedMode
+ && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
+ // If the docked stack exists, don't resize non-floating stacks independently of the
+ // size computed from the docked stack size (otherwise they will be out of sync)
+ return;
+ }
+
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
+ mWindowManager.deferSurfaceLayout();
+ try {
+ if (stack.affectedBySplitScreenResize()) {
+ if (bounds == null && stack.inSplitScreenWindowingMode()) {
+ // null bounds = fullscreen windowing mode...at least for now.
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ } else if (splitScreenActive) {
+ // If we are in split-screen mode and this stack support split-screen, then
+ // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
+ stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+ }
+ stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
+ if (!deferResume) {
+ stack.ensureVisibleActivitiesConfigurationLocked(
+ stack.topRunningActivityLocked(), preserveWindows);
+ }
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ /**
+ * Move stack with all its existing content to specified display.
+ * @param stackId Id of stack to move.
+ * @param displayId Id of display to move stack to.
+ * @param onTop Indicates whether container should be place on top or on bottom.
+ */
+ void moveStackToDisplay(int stackId, int displayId, boolean onTop) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId="
+ + displayId);
+ }
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ throw new IllegalArgumentException("moveStackToDisplay: Unknown stackId="
+ + stackId);
+ }
+
+ final ActivityDisplay currentDisplay = stack.getDisplay();
+ if (currentDisplay == null) {
+ throw new IllegalStateException("moveStackToDisplay: Stack with stack=" + stack
+ + " is not attached to any display.");
+ }
+
+ if (currentDisplay.mDisplayId == displayId) {
+ throw new IllegalArgumentException("Trying to move stack=" + stack
+ + " to its current displayId=" + displayId);
+ }
+
+ stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
+ // TODO(multi-display): resize stacks properly if moved from split-screen.
+ }
+
+ boolean moveTopStackActivityToPinnedStack(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ throw new IllegalArgumentException(
+ "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
+ }
+
+ final ActivityRecord r = stack.topRunningActivityLocked();
+ if (r == null) {
+ Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
+ + " in stack=" + stack);
+ return false;
+ }
+
+ if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
+ Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for "
+ + " r=" + r);
+ return false;
+ }
+
+ moveActivityToPinnedStack(r, null /* sourceBounds */, 0f /* aspectRatio */,
+ "moveTopActivityToPinnedStack");
+ return true;
+ }
+
+ void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
+ String reason) {
+
+ mWindowManager.deferSurfaceLayout();
+
+ final ActivityDisplay display = r.getActivityStack().getDisplay();
+ PinnedActivityStack stack = display.getPinnedStack();
+
+ // This will clear the pinned stack by moving an existing task to the full screen stack,
+ // ensuring only one task is present.
+ if (stack != null) {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+ }
+
+ // Need to make sure the pinned stack exist so we can resize it below...
+ stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
+
+ // Calculate the target bounds here before the task is reparented back into pinned windowing
+ // mode (which will reset the saved bounds)
+ final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
+ try {
+ final TaskRecord task = r.getTaskRecord();
+ // 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.getRequestedOverrideBounds(), null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
+
+ if (task.mActivities.size() == 1) {
+ // Defer resume until below, and do not schedule PiP changes until we animate below
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+ false /* schedulePictureInPictureModeChange */, reason);
+ } else {
+ // There are multiple activities in the task and moving the top activity should
+ // reveal/leave the other activities in their original task.
+
+ // Currently, we don't support reparenting activities across tasks in two different
+ // stacks, so instead, just create a new task in the same stack, reparent the
+ // activity into that task, and then reparent the whole task to the new stack. This
+ // ensures that all the necessary work to migrate states in the old and new stacks
+ // is also done.
+ final TaskRecord newTask = task.getStack().createTaskRecord(
+ mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), r.info,
+ r.intent, null, null, true);
+ r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+ // Defer resume until below, and do not schedule PiP changes until we animate below
+ newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+ }
+
+ // Reset the state that indicates it can enter PiP while pausing after we've moved it
+ // to the pinned stack
+ r.supportsEnterPipOnTaskSwitch = false;
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ }
+
+ stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
+ true /* fromFullscreen */);
+
+ // Update the visibility of all activities after the they have been reparented to the new
+ // stack. This MUST run after the animation above is scheduled to ensure that the windows
+ // drawn signal is scheduled after the bounds animation start call on the bounds animator
+ // thread.
+ ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ resumeFocusedStacksTopActivities();
+
+ mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+ }
+
+ void executeAppTransitionForAllDisplay() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ display.getWindowContainerController().executeAppTransition();
+ }
+ }
+
+ void setDockedStackMinimized(boolean minimized) {
+ // Get currently focused stack before setting mIsDockMinimized. We do this because if
+ // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+ // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+ final ActivityStack current = getTopDisplayFocusedStack();
+ mIsDockMinimized = minimized;
+ if (mIsDockMinimized) {
+ if (current.inSplitScreenPrimaryWindowingMode()) {
+ // The primary split-screen stack can't be focused while it is minimize, so move
+ // focus to something else.
+ current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
+ }
+ }
+ }
+
+ ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
+ mTmpFindTaskResult.clear();
+
+ // Looking up task on preferred display first
+ final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
+ if (preferredDisplay != null) {
+ preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
+ }
+
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (display.mDisplayId == preferredDisplayId) {
+ continue;
+ }
+
+ display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
+ }
+
+ if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
+ return mTmpFindTaskResult.mRecord;
+ }
+
+ /**
+ * Finish the topmost activities in all stacks that belong to the crashed app.
+ * @param app The app that crashed.
+ * @param reason Reason to perform this action.
+ * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+ */
+ int finishTopCrashedActivities(WindowProcessController app, String reason) {
+ TaskRecord finishedTask = null;
+ ActivityStack focusedStack = getTopDisplayFocusedStack();
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ // It is possible that request to finish activity might also remove its task and stack,
+ // so we need to be careful with indexes in the loop and check child count every time.
+ for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
+ if (stack == focusedStack || finishedTask == null) {
+ finishedTask = t;
+ }
+ }
+ }
+ return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
+ }
+
+ boolean resumeFocusedStacksTopActivities() {
+ return resumeFocusedStacksTopActivities(null, null, null);
+ }
+
+ boolean resumeFocusedStacksTopActivities(
+ ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+
+ if (!mStackSupervisor.readyToResume()) {
+ return false;
+ }
+
+ if (targetStack != null && (targetStack.isTopStackOnDisplay()
+ || getTopDisplayFocusedStack() == targetStack)) {
+ return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+ }
+
+ // Resume all top activities in focused stacks on all displays.
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack == null) {
+ continue;
+ }
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
+ if (r == null || !r.isState(RESUMED)) {
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
+ } else if (r.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront operation.
+ focusedStack.executeAppTransition(targetOptions);
+ }
+ }
+
+ return false;
+ }
+
+ void applySleepTokens(boolean applyToStacks) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ // Set the sleeping state of the display.
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final boolean displayShouldSleep = display.shouldSleep();
+ if (displayShouldSleep == display.isSleeping()) {
+ continue;
+ }
+ display.setIsSleeping(displayShouldSleep);
+
+ if (!applyToStacks) {
+ continue;
+ }
+
+ // Set the sleeping state of the stacks on the display.
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (displayShouldSleep) {
+ stack.goToSleepIfPossible(false /* shuttingDown */);
+ } else {
+ stack.awakeFromSleepingLocked();
+ if (stack.isFocusedStackOnDisplay()
+ && !mStackSupervisor.getKeyguardController()
+ .isKeyguardOrAodShowing(display.mDisplayId)) {
+ // If the keyguard is unlocked - resume immediately.
+ // It is possible that the display will not be awake at the time we
+ // process the keyguard going away, which can happen before the sleep token
+ // is released. As a result, it is important we resume the activity here.
+ resumeFocusedStacksTopActivities();
+ }
+ }
+ }
+
+ if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) {
+ continue;
+ }
+ // The display is awake now, so clean up the going to sleep list.
+ for (Iterator<ActivityRecord> it =
+ mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+ final ActivityRecord r = it.next();
+ if (r.getDisplayId() == display.mDisplayId) {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ protected <T extends ActivityStack> T getStack(int stackId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.get(i).getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ /** @see ActivityDisplay#getStack(int, int) */
+ private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ private ActivityManager.StackInfo getStackInfo(ActivityStack stack) {
+ final int displayId = stack.mDisplayId;
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ ActivityManager.StackInfo info = new ActivityManager.StackInfo();
+ stack.getWindowContainerBounds(info.bounds);
+ info.displayId = displayId;
+ info.stackId = stack.mStackId;
+ info.userId = stack.mCurrentUser;
+ info.visible = stack.shouldBeVisible(null);
+ // A stack might be not attached to a display.
+ info.position = display != null ? display.getIndexOf(stack) : 0;
+ info.configuration.setTo(stack.getConfiguration());
+
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ final int numTasks = tasks.size();
+ int[] taskIds = new int[numTasks];
+ String[] taskNames = new String[numTasks];
+ Rect[] taskBounds = new Rect[numTasks];
+ int[] taskUserIds = new int[numTasks];
+ for (int i = 0; i < numTasks; ++i) {
+ final TaskRecord task = tasks.get(i);
+ taskIds[i] = task.taskId;
+ taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+ : task.realActivity != null ? task.realActivity.flattenToString()
+ : task.getTopActivity() != null ? task.getTopActivity().packageName
+ : "unknown";
+ taskBounds[i] = new Rect();
+ task.getWindowContainerBounds(taskBounds[i]);
+ taskUserIds[i] = task.userId;
+ }
+ info.taskIds = taskIds;
+ info.taskNames = taskNames;
+ info.taskBounds = taskBounds;
+ info.taskUserIds = taskUserIds;
+
+ final ActivityRecord top = stack.topRunningActivityLocked();
+ info.topActivity = top != null ? top.intent.getComponent() : null;
+ return info;
+ }
+
+ ActivityManager.StackInfo getStackInfo(int stackId) {
+ ActivityStack stack = getStack(stackId);
+ if (stack != null) {
+ return getStackInfo(stack);
+ }
+ return null;
+ }
+
+ ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+ final ActivityStack stack = getStack(windowingMode, activityType);
+ return (stack != null) ? getStackInfo(stack) : null;
+ }
+
+ ArrayList<ActivityManager.StackInfo> getAllStackInfos() {
+ ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ list.add(getStackInfo(stack));
+ }
+ }
+ return list;
+ }
+
+ void deferUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ stack.deferUpdateBounds();
+ }
+ }
+
+ void continueUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ stack.continueUpdateBounds();
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
+ synchronized (mService.mGlobalLock) {
+ getActivityDisplayOrCreate(displayId);
+ // Do not start home before booting, or it may accidentally finish booting before it
+ // starts. Instead, we expect home activities to be launched when the system is ready
+ // (ActivityManagerService#systemReady).
+ if (mService.isBooted() || mService.isBooting()) {
+ startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
+ if (displayId == DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("Can't remove the primary display.");
+ }
+
+ synchronized (mService.mGlobalLock) {
+ final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+ if (activityDisplay == null) {
+ return;
+ }
+
+ activityDisplay.remove();
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
+ synchronized (mService.mGlobalLock) {
+ final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+ if (activityDisplay != null) {
+ activityDisplay.onDisplayChanged();
+ }
+ }
+ }
+
+ /** Update lists of UIDs that are present on displays and have access to them. */
+ void updateUIDsPresentOnDisplay() {
+ mDisplayAccessUIDs.clear();
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ // Only bother calculating the whitelist for private displays
+ if (activityDisplay.isPrivate()) {
+ mDisplayAccessUIDs.append(
+ activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+ }
+ }
+ // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+ mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+ }
+
+ ActivityStack findStackBehind(ActivityStack stack) {
+ final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
+ if (display != null) {
+ for (int i = display.getChildCount() - 1; i >= 0; i--) {
+ if (display.getChildAt(i) == stack && i > 0) {
+ return display.getChildAt(i - 1);
+ }
+ }
+ }
+ throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+ + " in=" + display);
+ }
+
+ @Override
+ protected int getChildCount() {
+ return mActivityDisplays.size();
+ }
+
+ @Override
+ protected ActivityDisplay getChildAt(int index) {
+ return mActivityDisplays.get(index);
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return null;
+ }
+
+ // TODO: remove after object merge with RootWindowContainer
+ void onChildPositionChanged(DisplayWindowController childController, 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);
+ }
+ }
+
+ /** Change the z-order of the given display. */
+ private void positionChildAt(ActivityDisplay display, int position) {
+ if (position >= mActivityDisplays.size()) {
+ position = mActivityDisplays.size() - 1;
+ } else if (position < 0) {
+ position = 0;
+ }
+
+ if (mActivityDisplays.isEmpty()) {
+ mActivityDisplays.add(display);
+ } else if (mActivityDisplays.get(position) != display) {
+ mActivityDisplays.remove(display);
+ mActivityDisplays.add(position, display);
+ }
+ }
+
+ @VisibleForTesting
+ void addChild(ActivityDisplay activityDisplay, int position) {
+ positionChildAt(activityDisplay, position);
+ mRootWindowContainer.positionChildAt(position,
+ activityDisplay.getWindowContainerController().mContainer);
+ }
+
+ void removeChild(ActivityDisplay activityDisplay) {
+ // The caller must tell the controller of {@link ActivityDisplay} to release its container
+ // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
+ mActivityDisplays.remove(activityDisplay);
+ }
+
+ Configuration getDisplayOverrideConfiguration(int displayId) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("No display found with id: " + displayId);
+ }
+
+ return activityDisplay.getRequestedOverrideConfiguration();
+ }
+
+ void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("No display found with id: " + displayId);
+ }
+
+ activityDisplay.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+ }
+
+ void prepareForShutdown() {
+ for (int i = 0; i < mActivityDisplays.size(); i++) {
+ createSleepToken("shutdown", mActivityDisplays.get(i).mDisplayId);
+ }
+ }
+
+ ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) {
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException("Invalid display: " + displayId);
+ }
+
+ final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+ mSleepTokens.add(token);
+ display.mAllSleepTokens.add(token);
+ return token;
+ }
+
+ private void removeSleepToken(SleepTokenImpl token) {
+ mSleepTokens.remove(token);
+
+ final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
+ if (display != null) {
+ display.mAllSleepTokens.remove(token);
+ if (display.mAllSleepTokens.isEmpty()) {
+ mService.updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.addStartingWindowsForVisibleActivities(taskSwitch);
+ }
+ }
+ }
+
+ void invalidateTaskLayers() {
+ mTaskLayersChanged = true;
+ }
+
+ void rankTaskLayersIfNeeded() {
+ if (!mTaskLayersChanged) {
+ return;
+ }
+ mTaskLayersChanged = false;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ int baseLayer = 0;
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ baseLayer += stack.rankTaskLayers(baseLayer);
+ }
+ }
+ }
+
+ void clearOtherAppTimeTrackers(AppTimeTracker except) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.clearOtherAppTimeTrackers(except);
+ }
+ }
+ }
+
+ void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.scheduleDestroyActivities(app, reason);
+ }
+ }
+ }
+
+ void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
+ // Tasks is non-null only if two or more tasks are found.
+ ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
+ if (tasks == null) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
+ return;
+ }
+ // If we have activities in multiple tasks that are in a position to be destroyed,
+ // let's iterate through the tasks and release the oldest one.
+ final int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final int stackCount = display.getChildCount();
+ // Step through all stacks starting from behind, to hit the oldest things first.
+ for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ // Try to release activities in this stack; if we manage to, we are done.
+ if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Tries to put all activity stacks to sleep. Returns true if all stacks were
+ // successfully put to sleep.
+ boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
+ boolean allSleep = true;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (allowDelay) {
+ allSleep &= stack.goToSleepIfPossible(shuttingDown);
+ } else {
+ stack.goToSleep();
+ }
+ }
+ }
+ return allSleep;
+ }
+
+ void handleAppCrash(WindowProcessController app) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.handleAppCrash(app);
+ }
+ }
+ }
+
+ ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord ar = stack.findActivityLocked(
+ intent, info, compareIntentFilters);
+ if (ar != null) {
+ return ar;
+ }
+ }
+ }
+ return null;
+ }
+
+ boolean hasAwakeDisplay() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (!display.shouldSleep()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+ return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
+ }
+
+ /**
+ * Returns the right stack to use for launching factoring in all the input parameters.
+ *
+ * @param r The activity we are trying to launch. Can be null.
+ * @param options The activity options used to the launch. Can be null.
+ * @param candidateTask The possible task the activity might be launched in. Can be null.
+ * @params launchParams The resolved launch params to use.
+ *
+ * @return The stack to use for the launch or INVALID_STACK_ID.
+ */
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ int taskId = INVALID_TASK_ID;
+ int displayId = INVALID_DISPLAY;
+ //Rect bounds = null;
+
+ // We give preference to the launch preference in activity options.
+ if (options != null) {
+ taskId = options.getLaunchTaskId();
+ displayId = options.getLaunchDisplayId();
+ }
+
+ // First preference for stack goes to the task Id set in the activity options. Use the stack
+ // associated with that if possible.
+ if (taskId != INVALID_TASK_ID) {
+ // Temporarily set the task id to invalid in case in re-entry.
+ options.setLaunchTaskId(INVALID_TASK_ID);
+ final TaskRecord task = anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+ options.setLaunchTaskId(taskId);
+ if (task != null) {
+ return task.getStack();
+ }
+ }
+
+ final int activityType = resolveActivityType(r, options, candidateTask);
+ T stack;
+
+ // Next preference for stack goes to the display Id set the candidate display.
+ if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
+ displayId = launchParams.mPreferredDisplayId;
+ }
+ if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+ if (r != null) {
+ stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
+ launchParams);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ final ActivityDisplay display = getActivityDisplayOrCreate(displayId);
+ if (display != null) {
+ stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ }
+
+ // Give preference to the stack and display of the input task and activity if they match the
+ // mode we want to launch into.
+ stack = null;
+ ActivityDisplay display = null;
+ if (candidateTask != null) {
+ stack = candidateTask.getStack();
+ }
+ if (stack == null && r != null) {
+ stack = r.getActivityStack();
+ }
+ if (stack != null) {
+ display = stack.getDisplay();
+ if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
+ int windowingMode = launchParams != null ? launchParams.mWindowingMode
+ : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+ if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+ activityType);
+ }
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+ && display.getSplitScreenPrimaryStack() == stack
+ && candidateTask == stack.topTask()) {
+ // This is a special case when we try to launch an activity that is currently on
+ // top of split-screen primary stack, but is targeting split-screen secondary.
+ // In this case we don't want to move it to another stack.
+ // TODO(b/78788972): Remove after differentiating between preferred and required
+ // launch options.
+ return stack;
+ }
+ }
+ }
+
+ if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
+ display = getDefaultDisplay();
+ }
+
+ return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+ }
+
+ /** @return true if activity record is null or can be launched on provided display. */
+ private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
+ if (r == null) {
+ return true;
+ }
+ return r.canBeLaunchedOnDisplay(displayId);
+ }
+
+ /**
+ * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+ * If there is no such stack, new dynamic stack can be created.
+ * @param displayId Target display.
+ * @param r Activity that should be launched there.
+ * @param candidateTask The possible task the activity might be put in.
+ * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
+ */
+ private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+ @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException(
+ "Display with displayId=" + displayId + " not found.");
+ }
+
+ if (!r.canBeLaunchedOnDisplay(displayId)) {
+ return null;
+ }
+
+ // If {@code r} is already in target display and its task is the same as the candidate task,
+ // the intention should be getting a launch stack for the reusable activity, so we can use
+ // the existing stack.
+ if (r.getDisplayId() == displayId && r.getTaskRecord() == candidateTask) {
+ return candidateTask.getStack();
+ }
+
+ // Return the topmost valid stack on the display.
+ for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = activityDisplay.getChildAt(i);
+ if (isValidLaunchStack(stack, r)) {
+ return stack;
+ }
+ }
+
+ // If there is no valid stack on the external display - check if new dynamic stack will do.
+ if (displayId != DEFAULT_DISPLAY) {
+ final int windowingMode;
+ if (launchParams != null) {
+ // When launch params is not null, we always defer to its windowing mode. Sometimes
+ // it could be unspecified, which indicates it should inherit windowing mode from
+ // display.
+ windowingMode = launchParams.mWindowingMode;
+ } else {
+ windowingMode = options != null ? options.getLaunchWindowingMode()
+ : r.getWindowingMode();
+ }
+ final int activityType =
+ options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
+ ? options.getLaunchActivityType() : r.getActivityType();
+ return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
+ }
+
+ Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
+ return null;
+ }
+
+ ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+ @Nullable ActivityOptions options,
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
+ launchParams);
+ }
+
+ // TODO: Can probably be consolidated into getLaunchStack()...
+ private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) {
+ switch (stack.getActivityType()) {
+ case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+ case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+ case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ }
+ // There is a 1-to-1 relationship between stack and task when not in
+ // primary split-windowing mode.
+ if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ return false;
+ } else {
+ return r.supportsSplitScreenWindowingMode();
+ }
+ }
+
+ int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable TaskRecord task) {
+ // Preference is given to the activity type for the activity then the task since the type
+ // once set shouldn't change.
+ int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+ activityType = task.getActivityType();
+ }
+ if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+ return activityType;
+ }
+ if (options != null) {
+ activityType = options.getLaunchActivityType();
+ }
+ return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
+ }
+
+ /**
+ * Get next focusable stack in the system. This will search through the stack on the same
+ * display as the current focused stack, looking for a focusable and visible stack, different
+ * from the target stack. If no valid candidates will be found, it will then go through all
+ * displays and stacks in last-focused order.
+ *
+ * @param currentFocus The stack that previously had focus.
+ * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
+ * candidate.
+ * @return Next focusable {@link ActivityStack}, {@code null} if not found.
+ */
+ ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus,
+ boolean ignoreCurrent) {
+ // First look for next focusable stack on the same display
+ final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+ final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+ currentFocus, ignoreCurrent);
+ if (preferredFocusableStack != null) {
+ return preferredFocusableStack;
+ }
+ if (preferredDisplay.supportsSystemDecorations()) {
+ // Stop looking for focusable stack on other displays because the preferred display
+ // supports system decorations. Home activity would be launched on the same display if
+ // no focusable stack found.
+ return null;
+ }
+
+ // Now look through all displays
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display == preferredDisplay) {
+ // We've already checked this one
+ continue;
+ }
+ final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+ ignoreCurrent);
+ if (nextFocusableStack != null) {
+ return nextFocusableStack;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get next valid stack for launching provided activity in the system. This will search across
+ * displays and stacks in last-focused order for a focusable and visible stack, except those
+ * that are on a currently focused display.
+ *
+ * @param r The activity that is being launched.
+ * @param currentFocus The display that previously had focus and thus needs to be ignored when
+ * searching for the next candidate.
+ * @return Next valid {@link ActivityStack}, null if not found.
+ */
+ ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display.mDisplayId == currentFocus) {
+ continue;
+ }
+ final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
+ null /* options */, null /* launchParams */);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ boolean handleAppDied(WindowProcessController app) {
+ boolean hasVisibleActivities = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ hasVisibleActivities |= stack.handleAppDiedLocked(app);
+ }
+ }
+ return hasVisibleActivities;
+ }
+
+ void closeSystemDialogs() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.closeSystemDialogsLocked();
+ }
+ }
+ }
+
+ /** @return true if some activity was finished (or would have finished if doit were true). */
+ boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
+ boolean doit, boolean evenPersistent, int userId) {
+ boolean didSomething = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (stack.finishDisabledPackageActivitiesLocked(
+ packageName, filterByClasses, doit, evenPersistent, userId)) {
+ didSomething = true;
+ }
+ }
+ }
+ return didSomething;
+ }
+
+ void updateActivityApplicationInfo(ApplicationInfo aInfo) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.updateActivityApplicationInfoLocked(aInfo);
+ }
+ }
+ }
+
+ void finishVoiceTask(IVoiceInteractionSession session) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final int numStacks = display.getChildCount();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.finishVoiceTask(session);
+ }
+ }
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
+ }
+ }
+
+ ActivityRecord topRunningActivity() {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+ if (topActivity != null) {
+ return topActivity;
+ }
+ }
+ return null;
+ }
+
+ boolean allResumedActivitiesIdle() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ // TODO(b/117135575): Check resumed activities on all visible stacks.
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (display.isSleeping()) {
+ // No resumed activities while display is sleeping.
+ continue;
+ }
+
+ // If the focused stack is not null or not empty, there should have some activities
+ // resuming or resumed. Make sure these activities are idle.
+ final ActivityStack stack = display.getFocusedStack();
+ if (stack == null || stack.numActivities() == 0) {
+ continue;
+ }
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity == null || !resumedActivity.idle) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+ + stack.mStackId + " " + resumedActivity + " not idle");
+ }
+ return false;
+ }
+ }
+ // Send launch end powerhint when idle
+ sendPowerHintForLaunchEndIfNeeded();
+ return true;
+ }
+
+ boolean allResumedActivitiesVisible() {
+ boolean foundResumed = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord r = stack.getResumedActivity();
+ if (r != null) {
+ if (!r.nowVisible
+ || mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+ return false;
+ }
+ foundResumed = true;
+ }
+ }
+ }
+ return foundResumed;
+ }
+
+ boolean allPausedActivitiesComplete() {
+ boolean pausing = true;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord r = stack.mPausingActivity;
+ if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES,
+ "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
+ pausing = false;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ return pausing;
+ }
+
+ /**
+ * Find all visible task stacks containing {@param userId} and intercept them with an activity
+ * to block out the contents and possibly start a credential-confirming intent.
+ *
+ * @param userId user handle for the locked managed profile.
+ */
+ void lockAllProfileTasks(@UserIdInt int userId) {
+ mWindowManager.deferSurfaceLayout();
+ try {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final List<TaskRecord> tasks = stack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+ final TaskRecord task = tasks.get(taskNdx);
+
+ // Check the task for a top activity belonging to userId, or returning a
+ // result to an activity belonging to userId. Example case: a document
+ // picker for personal files, opened by a work app, should still get locked.
+ if (taskTopActivityIsUser(task, userId)) {
+ mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
+ task.taskId, userId);
+ }
+ }
+ }
+ }
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ /**
+ * Detects whether we should show a lock screen in front of this task for a locked user.
+ * <p>
+ * We'll do this if either of the following holds:
+ * <ul>
+ * <li>The top activity explicitly belongs to {@param userId}.</li>
+ * <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+ * </ul>
+ *
+ * @return {@code true} if the top activity looks like it belongs to {@param userId}.
+ */
+ private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+ // To handle the case that work app is in the task but just is not the top one.
+ final ActivityRecord activityRecord = task.getTopActivity();
+ final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+ return (activityRecord != null && activityRecord.mUserId == userId)
+ || (resultTo != null && resultTo.mUserId == userId);
+ }
+
+ void cancelInitializingActivities() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.cancelInitializingActivities();
+ }
+ }
+ }
+
+ TaskRecord anyTaskForId(int id) {
+ return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+ }
+
+ TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
+ return anyTaskForId(id, matchMode, null, !ON_TOP);
+ }
+
+ /**
+ * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
+ * @param id Id of the task we would like returned.
+ * @param matchMode The mode to match the given task id in.
+ * @param aOptions The activity options to use for restoration. Can be null.
+ * @param onTop If the stack for the task should be the topmost on the display.
+ */
+ TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode,
+ @Nullable ActivityOptions aOptions, boolean onTop) {
+ // If options are set, ensure that we are attempting to actually restore a task
+ if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+ throw new IllegalArgumentException("Should not specify activity options for non-restore"
+ + " lookup");
+ }
+
+ int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final TaskRecord task = stack.taskForIdLocked(id);
+ if (task == null) {
+ continue;
+ }
+ if (aOptions != null) {
+ // Resolve the stack the task should be placed in now based on options
+ // and reparent if needed.
+ final ActivityStack launchStack =
+ getLaunchStack(null, aOptions, task, onTop);
+ if (launchStack != null && stack != launchStack) {
+ final int reparentMode = onTop
+ ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+ task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+ "anyTaskForId");
+ }
+ }
+ return task;
+ }
+ }
+
+ // If we are matching stack tasks only, return now
+ if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
+ return null;
+ }
+
+ // Otherwise, check the recent tasks and return if we find it there and we are not restoring
+ // the task from recents
+ if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
+ final TaskRecord task = mStackSupervisor.mRecentTasks.getTask(id);
+
+ if (task == null) {
+ if (DEBUG_RECENTS) {
+ Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+ }
+
+ return null;
+ }
+
+ if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+ return task;
+ }
+
+ // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) {
+ if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
+ "Couldn't restore task id=" + id + " found in recents");
+ return null;
+ }
+ if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
+ return task;
+ }
+
+ ActivityRecord isInAnyStack(IBinder token) {
+ int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord r = stack.isInStackLocked(token);
+ if (r != null) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
+ @WindowConfiguration.ActivityType int ignoreActivityType,
+ @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
+ boolean allowed) {
+ mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+ ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
+ }
+
+ void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
+ boolean sendHint = forceSend;
+
+ if (!sendHint) {
+ // Send power hint if we don't know what we're launching yet
+ sendHint = targetActivity == null || targetActivity.app == null;
+ }
+
+ if (!sendHint) { // targetActivity != null
+ // Send power hint when the activity's process is different than the current resumed
+ // activity on all displays, or if there are no resumed activities in the system.
+ boolean noResumedActivities = true;
+ boolean allFocusedProcessesDiffer = true;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+ final WindowProcessController resumedActivityProcess =
+ resumedActivity == null ? null : resumedActivity.app;
+
+ noResumedActivities &= resumedActivityProcess == null;
+ if (resumedActivityProcess != null) {
+ allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+ }
+ }
+ sendHint = noResumedActivities || allFocusedProcessesDiffer;
+ }
+
+ if (sendHint && mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
+ mPowerHintSent = true;
+ }
+ }
+
+ void sendPowerHintForLaunchEndIfNeeded() {
+ // Trigger launch power hint if activity is launched
+ if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
+ mPowerHintSent = false;
+ }
+ }
+
+ private void calculateDefaultMinimalSizeOfResizeableTasks() {
+ final Resources res = mService.mContext.getResources();
+ final float minimalSize = res.getDimension(
+ com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ final DisplayMetrics dm = res.getDisplayMetrics();
+
+ mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
+ }
+
+ /**
+ * Dumps the activities matching the given {@param name} in the either the focused stack
+ * or all visible stacks if {@param dumpVisibleStacks} is true.
+ */
+ ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
+ boolean dumpFocusedStackOnly) {
+ if (dumpFocusedStackOnly) {
+ return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+ } else {
+ ArrayList<ActivityRecord> activities = new ArrayList<>();
+ int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+ activities.addAll(stack.getDumpActivitiesLocked(name));
+ }
+ }
+ }
+ return activities;
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ display.dump(pw, prefix);
+ }
+ }
+
+ /**
+ * Dump all connected displays' configurations.
+ * @param prefix Prefix to apply to each line of the dump.
+ */
+ void dumpDisplayConfigs(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.println("Display override configurations:");
+ final int displayCount = mActivityDisplays.size();
+ 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.getRequestedOverrideConfiguration());
+ }
+ }
+
+ public void dumpDisplays(PrintWriter pw) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ pw.print("[id:" + display.mDisplayId + " stacks:");
+ display.dumpStacks(pw);
+ pw.print("]");
+ }
+ }
+
+ boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
+ String dumpPackage) {
+ boolean printed = false;
+ boolean needSep = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+ pw.println(" (activities from top to bottom):");
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ pw.println();
+ pw.println(" Stack #" + stack.mStackId
+ + ": type=" + activityTypeToString(stack.getActivityType())
+ + " mode=" + windowingModeToString(stack.getWindowingMode()));
+ pw.println(" isSleeping=" + stack.shouldSleepActivities());
+ pw.println(" mBounds=" + stack.getRequestedOverrideBounds());
+
+ printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+ needSep);
+
+ printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false,
+ !dumpAll, false, dumpPackage, true,
+ " Running activities (most recent first):", null);
+
+ needSep = printed;
+ boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+ " mPausingActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = false;
+ }
+ pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
+ " mResumedActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = false;
+ }
+ if (dumpAll) {
+ pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+ " mLastPausedActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = true;
+ }
+ printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+ needSep, " mLastNoHistoryActivity: ");
+ }
+ needSep = printed;
+ }
+ printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity:");
+ }
+
+ printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
+ "Fin", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to finish:", null);
+ printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
+ "Stop", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to stop:", null);
+ printed |= dumpHistoryList(fd, pw,
+ mStackSupervisor.mActivitiesWaitingForVisibleActivity, " ", "Wait",
+ false, !dumpAll, false, dumpPackage, true,
+ " Activities waiting for another to become visible:", null);
+ printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities,
+ " ", "Sleep", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to sleep:", null);
+
+ return printed;
+ }
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ activityDisplay.writeToProto(proto, DISPLAYS);
+ }
+ mStackSupervisor.getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
+ // TODO(b/111541062): Update tests to look for resumed activities on all displays
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack != null) {
+ proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+ final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+ if (focusedActivity != null) {
+ focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ }
+ } else {
+ proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+ }
+ proto.write(IS_HOME_RECENTS_COMPONENT,
+ mStackSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
+ proto.end(token);
+ }
+
+ private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
+ private final String mTag;
+ private final long mAcquireTime;
+ private final int mDisplayId;
+
+ public SleepTokenImpl(String tag, int displayId) {
+ mTag = tag;
+ mDisplayId = displayId;
+ mAcquireTime = SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public void release() {
+ synchronized (mService.mGlobalLock) {
+ removeSleepToken(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{\"" + mTag + "\", display " + mDisplayId
+ + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b483fd3..3a5c578 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -82,12 +82,16 @@
import java.util.function.Consumer;
/** Root {@link WindowContainer} for the device. */
-class RootWindowContainer extends WindowContainer<DisplayContent> {
+class RootWindowContainer extends WindowContainer<DisplayContent>
+ implements ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
+ // TODO: Remove after object merge with RootActivityContainer.
+ private RootActivityContainer mRootActivityContainer;
+
private Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private float mScreenBrightness = -1;
@@ -145,6 +149,13 @@
mHandler = new MyHandler(service.mH.getLooper());
}
+ void setRootActivityContainer(RootActivityContainer container) {
+ mRootActivityContainer = container;
+ if (container != null) {
+ container.registerConfigurationChangeListener(this);
+ }
+ }
+
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
@@ -171,7 +182,7 @@
mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL;
if (mTopFocusedDisplayId != topFocusedDisplayId) {
mTopFocusedDisplayId = topFocusedDisplayId;
- mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
+ mWmService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
+ mTopFocusedDisplayId);
}
@@ -183,15 +194,13 @@
final boolean isTopFocusedDisplay =
topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId;
if (windowFocusChanged || isTopFocusedDisplay) {
- final Message msg = mService.mH.obtainMessage(
+ final Message msg = mWmService.mH.obtainMessage(
WindowManagerService.H.REPORT_FOCUS_CHANGE, dc);
msg.arg1 = topFocusedDisplayChanged ? 1 : 0;
- mService.mH.sendMessage(msg);
+ mWmService.mH.sendMessage(msg);
}
});
- final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
- mService.mInputManager.setFocusedWindow(
- topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+
return changed;
}
@@ -202,7 +211,7 @@
@Override
void onChildPositionChanged() {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
DisplayContent getDisplayContent(int displayId) {
@@ -224,27 +233,42 @@
final DisplayContent existing = getDisplayContent(displayId);
if (existing != null) {
+ initializeDisplayOverrideConfiguration(controller, existing);
existing.setController(controller);
return existing;
}
- final DisplayContent dc = new DisplayContent(display, mService, controller);
+ final DisplayContent dc = new DisplayContent(display, mWmService, controller);
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
- mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+ mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+ initializeDisplayOverrideConfiguration(controller, dc);
- if (mService.mDisplayManagerInternal != null) {
- mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
+ if (mWmService.mDisplayManagerInternal != null) {
+ mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayId, dc.getDisplayInfo());
dc.configureDisplayPolicy();
}
- mService.reconfigureDisplayLocked(dc);
+ mWmService.reconfigureDisplayLocked(dc);
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.getRequestedOverrideConfiguration());
+ }
+ }
+
boolean isLayoutNeeded() {
final int numDisplays = mChildren.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -332,13 +356,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
@@ -412,11 +436,11 @@
void removeReplacedWindows() {
if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
- mService.openSurfaceTransaction();
+ mWmService.openSurfaceTransaction();
try {
forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
} finally {
- mService.closeSurfaceTransaction("removeReplacedWindows");
+ mWmService.closeSurfaceTransaction("removeReplacedWindows");
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
}
}
@@ -464,7 +488,7 @@
final SparseIntArray pidCandidates = new SparseIntArray();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
mChildren.get(displayNdx).forAllWindows((w) -> {
- if (mService.mForceRemoves.contains(w)) {
+ if (mWmService.mForceRemoves.contains(w)) {
return;
}
final WindowStateAnimator wsa = w.mWinAnimator;
@@ -479,7 +503,7 @@
pids[i] = pidCandidates.keyAt(i);
}
try {
- if (mService.mActivityManager.killPids(pids, "Free memory", secure)) {
+ if (mWmService.mActivityManager.killPids(pids, "Free memory", secure)) {
killedApps = true;
}
} catch (RemoteException e) {
@@ -497,9 +521,8 @@
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
"RECOVER DESTROY", false);
winAnimator.destroySurface();
- if (winAnimator.mWin.mAppToken != null
- && winAnimator.mWin.mAppToken.getController() != null) {
- winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+ if (winAnimator.mWin.mAppToken != null) {
+ winAnimator.mWin.mAppToken.removeStartingWindow();
}
}
@@ -524,9 +547,9 @@
int i;
boolean updateInputWindowsNeeded = false;
- if (mService.mFocusMayChange) {
- mService.mFocusMayChange = false;
- updateInputWindowsNeeded = mService.updateFocusedWindowLocked(
+ if (mWmService.mFocusMayChange) {
+ mWmService.mFocusMayChange = false;
+ updateInputWindowsNeeded = mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
@@ -542,31 +565,31 @@
mUserActivityTimeout = -1;
mObscureApplicationContentOnSecondaryDisplays = false;
mSustainedPerformanceModeCurrent = false;
- mService.mTransactionSequence++;
+ mWmService.mTransactionSequence++;
// TODO(multi-display): recents animation & wallpaper need support multi-display.
- final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
- final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+ final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
+ final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
- mService.openSurfaceTransaction();
+ mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction(recoveringMemory);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
+ mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
- mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+ mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
// Defer starting the recents animation until the wallpaper has drawn
final RecentsAnimationController recentsAnimationController =
- mService.getRecentsAnimationController();
+ mWmService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
@@ -583,9 +606,9 @@
}
}
- if (mService.mFocusMayChange) {
- mService.mFocusMayChange = false;
- if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ if (mWmService.mFocusMayChange) {
+ mWmService.mFocusMayChange = false;
+ if (mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
updateInputWindowsNeeded = true;
}
@@ -599,23 +622,23 @@
handleResizingWindows();
- if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
+ if (DEBUG_ORIENTATION && mWmService.mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
if (mOrientationChangeComplete) {
- if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
- mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
- mService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
- mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
+ if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+ mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+ mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
+ mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
- mService.stopFreezingDisplayLocked();
+ mWmService.stopFreezingDisplayLocked();
}
// Destroy the surface of any windows that are no longer visible.
- i = mService.mDestroySurface.size();
+ i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
- WindowState win = mService.mDestroySurface.get(i);
+ WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
@@ -627,7 +650,7 @@
win.destroySurfaceUnchecked();
win.mWinAnimator.destroyPreservedSurfaceLocked();
} while (i > 0);
- mService.mDestroySurface.clear();
+ mWmService.mDestroySurface.clear();
}
// Time to remove any exiting tokens?
@@ -648,8 +671,8 @@
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
});
- mService.setHoldScreenLocked(mHoldScreen);
- if (!mService.mDisplayFrozen) {
+ mWmService.setHoldScreenLocked(mHoldScreen);
+ if (!mWmService.mDisplayFrozen) {
final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
? -1 : toBrightnessOverride(mScreenBrightness);
@@ -661,7 +684,7 @@
if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
- mService.mPowerManagerInternal.powerHint(
+ mWmService.mPowerManagerInternal.powerHint(
PowerHint.SUSTAINED_PERFORMANCE,
(mSustainedPerformanceModeEnabled ? 1 : 0));
}
@@ -671,22 +694,22 @@
mUpdateRotation = updateRotationUnchecked();
}
- if (mService.mWaitingForDrawnCallback != null
+ if (mWmService.mWaitingForDrawnCallback != null
|| (mOrientationChangeComplete && !isLayoutNeeded()
&& !mUpdateRotation)) {
- mService.checkDrawnWindowsLocked();
+ mWmService.checkDrawnWindowsLocked();
}
- final int N = mService.mPendingRemove.size();
+ final int N = mWmService.mPendingRemove.size();
if (N > 0) {
- if (mService.mPendingRemoveTmp.length < N) {
- mService.mPendingRemoveTmp = new WindowState[N + 10];
+ if (mWmService.mPendingRemoveTmp.length < N) {
+ mWmService.mPendingRemoveTmp = new WindowState[N + 10];
}
- mService.mPendingRemove.toArray(mService.mPendingRemoveTmp);
- mService.mPendingRemove.clear();
+ mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
+ mWmService.mPendingRemove.clear();
ArrayList<DisplayContent> displayList = new ArrayList();
for (i = 0; i < N; i++) {
- final WindowState w = mService.mPendingRemoveTmp[i];
+ final WindowState w = mWmService.mPendingRemoveTmp[i];
w.removeImmediately();
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
@@ -714,12 +737,13 @@
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
- "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
+ "performSurfacePlacementInner exit: animating="
+ + mWmService.mAnimator.isAnimating());
}
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
@@ -759,23 +783,23 @@
mObscuringWindow = null;
// TODO(multi-display): Support these features on secondary screens.
- final DisplayContent defaultDc = mService.getDefaultDisplayContentLocked();
+ final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
final int defaultDw = defaultInfo.logicalWidth;
final int defaultDh = defaultInfo.logicalHeight;
- if (mService.mWatermark != null) {
- mService.mWatermark.positionSurface(defaultDw, defaultDh);
+ if (mWmService.mWatermark != null) {
+ mWmService.mWatermark.positionSurface(defaultDw, defaultDh);
}
- if (mService.mStrictModeFlash != null) {
- mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ if (mWmService.mStrictModeFlash != null) {
+ mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
}
- if (mService.mCircularDisplayMask != null) {
- mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
- mService.getDefaultDisplayRotation());
+ if (mWmService.mCircularDisplayMask != null) {
+ mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
+ mWmService.getDefaultDisplayRotation());
}
- if (mService.mEmulatorDisplayOverlay != null) {
- mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
- mService.getDefaultDisplayRotation());
+ if (mWmService.mEmulatorDisplayOverlay != null) {
+ mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
+ mWmService.getDefaultDisplayRotation());
}
final int count = mChildren.size();
@@ -786,7 +810,7 @@
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
- mService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
+ mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
@@ -794,14 +818,14 @@
* Handles resizing windows during surface placement.
*/
private void handleResizingWindows() {
- for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
- WindowState win = mService.mResizingWindows.get(i);
+ for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
+ WindowState win = mWmService.mResizingWindows.get(i);
if (win.mAppFreezing) {
// Don't remove this window until rotation has completed.
continue;
}
win.reportResized();
- mService.mResizingWindows.remove(i);
+ mWmService.mResizingWindows.remove(i);
}
}
@@ -839,7 +863,7 @@
if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
mHoldScreen = w.mSession;
mHoldScreenWindow = w;
- } else if (DEBUG_KEEP_SCREEN_ON && w == mService.mLastWakeLockHoldingWindow) {
+ } else if (DEBUG_KEEP_SCREEN_ON && w == mWmService.mLastWakeLockHoldingWindow) {
Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding "
+ "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
+ Debug.getCallers(10));
@@ -888,7 +912,7 @@
boolean copyAnimToLayoutParams() {
boolean doRequest = false;
- final int bulkUpdateParams = mService.mAnimator.mBulkUpdateParams;
+ final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
mUpdateRotation = true;
doRequest = true;
@@ -897,8 +921,8 @@
mOrientationChangeComplete = false;
} else {
mOrientationChangeComplete = true;
- mLastWindowFreezeSource = mService.mAnimator.mLastWindowFreezeSource;
- if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+ mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
+ if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
doRequest = true;
}
}
@@ -924,12 +948,12 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_SCREEN_BRIGHTNESS_OVERRIDE:
- mService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
+ mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
msg.arg1);
break;
case SET_USER_ACTIVITY_TIMEOUT:
- mService.mPowerManagerInternal.setUserActivityTimeoutOverrideFromWindowManager(
- (Long) msg.obj);
+ mWmService.mPowerManagerInternal.
+ setUserActivityTimeoutOverrideFromWindowManager((Long) msg.obj);
break;
default:
break;
@@ -939,7 +963,7 @@
void dumpDisplayContents(PrintWriter pw) {
pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)");
- if (mService.mDisplayReady) {
+ if (mWmService.mDisplayReady) {
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final DisplayContent displayContent = mChildren.get(i);
@@ -992,7 +1016,7 @@
public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
final long token = proto.start(fieldId);
super.writeToProto(proto, WINDOW_CONTAINER, trim);
- if (mService.mDisplayReady) {
+ if (mWmService.mDisplayReady) {
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final DisplayContent displayContent = mChildren.get(i);
@@ -1015,9 +1039,8 @@
@Override
void positionChildAt(int position, DisplayContent child, boolean includingParents) {
super.positionChildAt(position, child, includingParents);
- final RootWindowContainerController controller = getController();
- if (controller != null) {
- controller.onChildPositionChanged(child, position);
+ if (mRootActivityContainer != null) {
+ mRootActivityContainer.onChildPositionChanged(child.getController(), position);
}
}
@@ -1027,13 +1050,8 @@
}
@Override
- RootWindowContainerController getController() {
- return (RootWindowContainerController) super.getController();
- }
-
- @Override
void scheduleAnimation() {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
/**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java
deleted file mode 100644
index 1176220..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerController.java
+++ /dev/null
@@ -1,46 +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;
-
-/**
- * Controller for the root container. This is created by activity manager to link activity
- * stack supervisor to the root window container they use in window manager.
- */
-public class RootWindowContainerController
- extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> {
-
- public RootWindowContainerController(RootWindowContainerListener listener) {
- super(listener, WindowManagerService.getInstance());
- synchronized (mGlobalLock) {
- mRoot.setController(this);
- }
- }
-
- void onChildPositionChanged(DisplayContent child, int position) {
- // This callback invokes to AM directly so here assumes AM lock is held. If there is another
- // path called only with WM lock, it should change to use handler to post or move outside of
- // WM lock with adding AM lock.
- mListener.onChildPositionChanged(child.getController(), position);
- }
-
- /** Move the display to the given position. */
- public void positionChildAt(DisplayWindowController child, int position) {
- synchronized (mGlobalLock) {
- mContainer.positionChildAt(position, child.mContainer);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/wm/RootWindowContainerListener.java
deleted file mode 100644
index f413e3f7..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java
+++ /dev/null
@@ -1,26 +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;
-
-/**
- * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to
- * the display container in activity manager.
- */
-public interface RootWindowContainerListener extends WindowContainerListener {
- /** Called when the z-order of display is changed. */
- void onChildPositionChanged(DisplayWindowController childController, int position);
-}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index df97027..3947bd4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -222,7 +222,7 @@
}
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) {
+ boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
mDisplayContent = displayContent;
@@ -234,7 +234,7 @@
final int originalWidth;
final int originalHeight;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (forceDefaultOrientation) {
+ if (fixedToUserRotation) {
// Emulated orientation.
mForceDefaultOrientation = true;
originalWidth = displayContent.mBaseDisplayWidth;
@@ -261,7 +261,7 @@
try {
mSurfaceControl = displayContent.makeOverlay()
.setName("ScreenshotSurface")
- .setSize(mWidth, mHeight)
+ .setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
.build();
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
* Removing the transform and the result of the {@link WindowState} layout are both tied to the
* {@link WindowState} next frame, such that they apply at the same time the client draws the
* window in the new orientation.
+ *
+ * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+ * it.
*/
- public void finish(WindowState win) {
+ public void finish(WindowState win, boolean timeout) {
mTransform.reset();
final Transaction t = win.getPendingTransaction();
t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
- if (win.mWinAnimator.mSurfaceController != null) {
+ if (win.mWinAnimator.mSurfaceController != null && !timeout) {
t.deferTransactionUntil(win.mSurfaceControl,
win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6838c55..37b5a7c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -50,6 +50,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.view.InsetsState;
import android.view.WindowManager;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -153,17 +154,21 @@
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+ DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+ InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
- outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
+ outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
+ outInsetsState);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
+ int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
+ InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
- new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */);
+ new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
+ outInsetsState);
}
@Override
@@ -182,7 +187,7 @@
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
- Surface outSurface) {
+ Surface outSurface, InsetsState outInsetsState) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -190,7 +195,7 @@
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, cutout,
- mergedConfiguration, outSurface);
+ mergedConfiguration, outSurface, outInsetsState);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 35264a2..8f18aa5 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -252,7 +252,7 @@
* before mContainer has been updated, any relevant properties (like {@param windowingMode})
* need to be passed in.
*/
- public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
+ public void adjustConfigurationForBounds(Rect bounds,
Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
boolean overrideHeight, float density, Configuration config,
Configuration parentConfig, int windowingMode) {
@@ -303,11 +303,9 @@
// 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,
+ intersectDisplayBoundsExcludeInsets(nonDecorBounds, bounds, mTmpNonDecorInsets,
mTmpDisplayBounds, overrideWidth, overrideHeight);
- intersectDisplayBoundsExcludeInsets(stableBounds,
- insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
+ intersectDisplayBoundsExcludeInsets(stableBounds, bounds, mTmpStableInsets,
mTmpDisplayBounds, overrideWidth, overrideHeight);
width = Math.min((int) (stableBounds.width() / density),
parentConfig.screenWidthDp);
@@ -323,7 +321,7 @@
config.screenWidthDp = width;
config.screenHeightDp = height;
config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
- insetBounds != null ? insetBounds : bounds, density, windowingMode);
+ bounds, density, windowingMode);
}
}
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index e97b366..82f2ad8 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -24,12 +23,9 @@
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class StrictModeFlash {
private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
@@ -46,7 +42,7 @@
try {
ctrl = dc.makeOverlay()
.setName("StrictModeFlash")
- .setSize(1, 1)
+ .setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
@@ -122,7 +118,7 @@
}
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setSize(dw, dh);
+ mSurfaceControl.setBufferSize(dw, dh);
mDrawNeeded = true;
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 31c0c7f..11068ce 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -302,8 +302,7 @@
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
.setParent(mAnimatable.getAnimationLeashParent())
- .setName(surface + " - animation-leash")
- .setSize(width, height);
+ .setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
if (!hidden) {
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index 0c9a14b..bbe5424 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -1,17 +1,6 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
- "options": [
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c9800f8..67657d0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,11 +27,11 @@
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;
@@ -66,8 +66,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;
@@ -119,7 +119,7 @@
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
setController(controller);
- setBounds(getOverrideBounds());
+ setBounds(getRequestedOverrideBounds());
mTaskDescription = taskDescription;
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
@@ -302,29 +302,28 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- updateSurfaceSize(dc);
adjustBoundsForDisplayChangeIfNeeded(dc);
super.onDisplayChanged(dc);
}
/**
- * 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) {
@@ -333,7 +332,7 @@
boolean isResizeable() {
return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
- || mService.mForceResizableTasks;
+ || mWmService.mForceResizableTasks;
}
/**
@@ -370,7 +369,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;
}
@@ -381,8 +380,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. */
@@ -408,6 +412,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,
@@ -582,7 +595,7 @@
}
boolean isTaskAnimating() {
- final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
+ final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
if (recentsAnim != null) {
if (recentsAnim.isAnimatingTask(this)) {
return true;
@@ -629,13 +642,13 @@
}
void forceWindowsScaleable(boolean force) {
- mService.openSurfaceTransaction();
+ mWmService.openSurfaceTransaction();
try {
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).forceWindowsScaleableInTransaction(force);
}
} finally {
- mService.closeSurfaceTransaction("forceWindowsScaleable");
+ mWmService.closeSurfaceTransaction("forceWindowsScaleable");
}
}
@@ -724,7 +737,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());
@@ -740,7 +753,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/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 3b3feac..bb3df02 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -283,8 +283,8 @@
void notifyActivityPinned(ActivityRecord r) {
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
- r.getTask().taskId, r.getStackId(), r.packageName);
- msg.sendingUid = r.userId;
+ r.getTaskRecord().taskId, r.getStackId(), r.packageName);
+ msg.sendingUid = r.mUserId;
forAllLocalListeners(mNotifyActivityPinned, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 117984a..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;
@@ -138,7 +139,7 @@
// STEP 1: Determine the display to launch the activity/task.
final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams);
outParams.mPreferredDisplayId = displayId;
- ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+ ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
if (DEBUG) {
appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
+ display.getWindowingMode());
@@ -300,12 +301,14 @@
displayId = stack.mDisplayId;
}
- if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) {
+ if (displayId != INVALID_DISPLAY
+ && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) {
displayId = currentParams.mPreferredDisplayId;
}
displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
- return (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) != null)
+ return (displayId != INVALID_DISPLAY
+ && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) != null)
? displayId : DEFAULT_DISPLAY;
}
@@ -524,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
@@ -606,7 +621,8 @@
|| displayBounds.height() < inOutBounds.height()) {
// There is no way for us to fit the bounds in the display without changing width
// or height. Just move the start to align with the display.
- final int layoutDirection = mSupervisor.getConfiguration().getLayoutDirection();
+ final int layoutDirection =
+ mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection();
final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
? displayBounds.width() - inOutBounds.width()
: 0;
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 8120dec..d50af38 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import android.annotation.NonNull;
import android.graphics.Bitmap;
@@ -330,7 +330,7 @@
// mWriteQueue.add(new TaskWriteQueueItem(task));
final int taskId = task.taskId;
- if (mStackSupervisor.anyTaskForIdLocked(taskId,
+ if (mService.mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 7182ad6..b88e581 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -33,6 +33,7 @@
import android.app.IActivityTaskManager;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
@@ -261,7 +262,7 @@
mClientChannel, mService.mAnimationHandler.getLooper(),
mService.mAnimator.getChoreographer());
- mDragApplicationHandle = new InputApplicationHandle(null);
+ mDragApplicationHandle = new InputApplicationHandle(new Binder());
mDragApplicationHandle.name = TAG;
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -269,7 +270,7 @@
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
display.getDisplayId());
mDragWindowHandle.name = TAG;
- mDragWindowHandle.inputChannel = mServerChannel;
+ mDragWindowHandle.token = mServerChannel.getToken();
mDragWindowHandle.layer = mService.getDragLayerLocked();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 51567a0..e15bf5b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -21,15 +21,19 @@
import android.annotation.Nullable;
import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.Display;
import android.view.IWindow;
+import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
import com.android.internal.annotations.GuardedBy;
import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
/**
* Controller for task positioning by drag.
@@ -39,10 +43,14 @@
private final InputManagerService mInputManager;
private final IActivityTaskManager mActivityManager;
private final Handler mHandler;
+ private SurfaceControl mInputSurface;
+ private DisplayContent mPositioningDisplay;
@GuardedBy("WindowManagerSerivce.mWindowMap")
private @Nullable TaskPositioner mTaskPositioner;
+ private final Rect mTmpClipRect = new Rect();
+
boolean isPositioningLocked() {
return mTaskPositioner != null;
}
@@ -59,6 +67,43 @@
mHandler = new Handler(looper);
}
+ void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
+ && mInputSurface != null) {
+ t.hide(mInputSurface);
+ }
+ }
+
+ void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
+ return;
+ }
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ if (mInputSurface == null) {
+ mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
+ .setContainerLayer(true)
+ .setName("Drag and Drop Input Consumer").build();
+ }
+
+ final InputWindowHandle h = getDragWindowHandleLocked();
+ if (h == null) {
+ Slog.w(TAG_WM, "Drag is in progress but there is no "
+ + "drag window handle.");
+ return;
+ }
+
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, h);
+ t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+ final Display display = dc.getDisplay();
+ final Point p = new Point();
+ display.getRealSize(p);
+
+ mTmpClipRect.set(0, 0, p.x, p.y);
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
+ }
+
boolean startMovingTask(IWindow window, float startX, float startY) {
WindowState win = null;
synchronized (mService.mGlobalLock) {
@@ -122,6 +167,7 @@
Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
return false;
}
+ mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
mTaskPositioner.register(displayContent);
@@ -138,9 +184,7 @@
if (!mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+ cleanUpTaskPositioner();
return false;
}
@@ -153,11 +197,21 @@
if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
synchronized (mService.mGlobalLock) {
- if (mTaskPositioner != null) {
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- }
+ cleanUpTaskPositioner();
+ mPositioningDisplay = null;
}
});
}
+
+ private void cleanUpTaskPositioner() {
+ final TaskPositioner positioner = mTaskPositioner;
+ if (positioner == null) {
+ return;
+ }
+
+ // We need to assign task positioner to null first to indicate that we're finishing task
+ // positioning.
+ mTaskPositioner = null;
+ positioner.unregister();
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index eec10ab..b6a6009 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -310,6 +310,11 @@
// 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();
@@ -447,6 +452,9 @@
}
mWindowContainerController = controller;
+ if (!mDisplayedBounds.isEmpty() && controller.mContainer != null) {
+ controller.mContainer.setOverrideDisplayedBounds(mDisplayedBounds);
+ }
}
void removeWindowContainer() {
@@ -472,8 +480,8 @@
}
mResizeMode = resizeMode;
mWindowContainerController.setResizeable(resizeMode);
- mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
void setTaskDockedResizing(boolean resizing) {
@@ -498,7 +506,7 @@
// 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;
}
@@ -544,10 +552,9 @@
// this won't cause tons of irrelevant windows being preserved because only
// activities in this task may experience a bounds change. Configs for other
// activities stay the same.
- mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
- preserveWindow);
+ mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow);
if (!kept) {
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -623,6 +630,7 @@
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+ final RootActivityContainer root = mService.mRootActivityContainer;
final WindowManagerService windowManager = mService.mWindowManager;
final ActivityStack sourceStack = getStack();
final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
@@ -655,7 +663,7 @@
boolean kept = true;
try {
final ActivityRecord r = topRunningActivityLocked();
- final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
+ final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack)
&& (topRunningActivityLocked() == r);
final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -712,12 +720,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 +739,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();
@@ -748,8 +756,8 @@
if (!deferResume) {
// The task might have already been running and its visibility needs to be synchronized
// with the visibility of the stack / windows.
- supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
- supervisor.resumeFocusedStacksTopActivitiesLocked();
+ root.ensureActivitiesVisible(null, 0, !mightReplaceWindow);
+ root.resumeFocusedStacksTopActivities();
}
// TODO: Handle incorrect request to move before the actual move, not after.
@@ -903,7 +911,7 @@
// Correct the activity intent for aliasing. The task record intent will always be based on
// the real activity that will be launched not the alias, so we need to use an intent with
// the component name pointing to the real activity not the alias in the activity record.
- intent.setComponent(r.realActivity);
+ intent.setComponent(r.mActivityComponent);
return intent.filterEquals(this.intent);
}
@@ -982,7 +990,7 @@
@Override
protected void onParentChanged() {
super.onParentChanged();
- mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+ mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
// Close up recents linked list.
@@ -1143,7 +1151,7 @@
}
boolean okToShowLocked() {
- // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
+ // NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is
// okay to show the activity when locked.
return mService.mStackSupervisor.isCurrentProfileLocked(userId)
|| topRunningActivityLocked() != null;
@@ -1182,7 +1190,7 @@
mActivities.add(newTop);
// Make sure window manager is aware of the position change.
- mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+ mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
updateEffectiveIntent();
setFrontOfTask();
@@ -1211,7 +1219,7 @@
* be in the current task or unparented to any task.
*/
void addActivityAtIndex(int index, ActivityRecord r) {
- TaskRecord task = r.getTask();
+ TaskRecord task = r.getTaskRecord();
if (task != null && task != this) {
throw new IllegalArgumentException("Can not add r=" + " to task=" + this
+ " current parent=" + task);
@@ -1264,17 +1272,15 @@
mService.notifyTaskPersisterLocked(this, false);
}
- // Sync. with window manager
- final AppWindowContainerController appController = r.getWindowContainerController();
- if (appController != null) {
+ 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(appController, index);
+ mWindowContainerController.positionChildAt(r.mAppWindowToken, index);
}
// Make sure the list of display UID whitelists is updated
// now that this record is in a new task.
- mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+ mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
/**
@@ -1287,7 +1293,7 @@
}
boolean removeActivity(ActivityRecord r, boolean reparenting) {
- if (r.getTask() != this) {
+ if (r.getTaskRecord() != this) {
throw new IllegalArgumentException(
"Activity=" + r + " does not belong to task=" + this);
}
@@ -1403,7 +1409,7 @@
if (r.finishing) {
continue;
}
- if (r.realActivity.equals(newR.realActivity)) {
+ if (r.mActivityComponent.equals(newR.mActivityComponent)) {
// Here it is! Now finish everything in front...
final ActivityRecord ret = r;
@@ -1540,7 +1546,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());
@@ -1561,13 +1567,13 @@
* the index within the history at which it's found, or < 0 if not found.
*/
final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
- final ComponentName realActivity = r.realActivity;
+ final ComponentName realActivity = r.mActivityComponent;
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord candidate = mActivities.get(activityNdx);
if (candidate.finishing) {
continue;
}
- if (candidate.realActivity.equals(realActivity)) {
+ if (candidate.mActivityComponent.equals(realActivity)) {
return candidate;
}
}
@@ -1683,9 +1689,9 @@
// to do this for the pinned stack as the bounds are controlled by the system.
if (!inPinnedWindowingMode()) {
final int defaultMinSizeDp =
- mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+ mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
final ActivityDisplay display =
- mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+ mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId);
final float density =
(float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
final int defaultMinSize = (int) (defaultMinSizeDp * density);
@@ -1703,7 +1709,7 @@
return;
}
- final Rect configBounds = getOverrideBounds();
+ final Rect configBounds = getRequestedOverrideBounds();
if (adjustWidth) {
if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
bounds.left = bounds.right - minWidth;
@@ -1733,10 +1739,16 @@
// (bounds == null), then leave the override config unset
final Configuration newOverrideConfig = new Configuration();
if (bounds != null) {
- newOverrideConfig.setTo(getOverrideConfiguration());
- mTmpRect.set(bounds);
+ newOverrideConfig.setTo(getRequestedOverrideConfiguration());
+ if (insetBounds != null && !insetBounds.isEmpty()) {
+ mTmpRect.set(insetBounds);
+ setDisplayedBounds(bounds);
+ } else {
+ mTmpRect.set(bounds);
+ setDisplayedBounds(null);
+ }
adjustForMinimalTaskDimensions(mTmpRect);
- computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
+ computeOverrideConfiguration(newOverrideConfig, mTmpRect,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
@@ -1769,13 +1781,13 @@
* @return True if the override configuration was updated.
*/
boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
- if (equivalentOverrideBounds(bounds)) {
+ if (equivalentRequestedOverrideBounds(bounds)) {
return false;
}
- final Rect currentBounds = getOverrideBounds();
+ final Rect currentBounds = getRequestedOverrideBounds();
- mTmpConfig.setTo(getOverrideConfiguration());
- final Configuration newConfig = getOverrideConfiguration();
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
+ final Configuration newConfig = getRequestedOverrideConfiguration();
final boolean matchParentBounds = bounds == null || bounds.isEmpty();
final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
@@ -1784,19 +1796,26 @@
setLastNonFullscreenBounds(currentBounds);
}
setBounds(null);
+ setDisplayedBounds(null);
newConfig.unset();
} else {
- mTmpRect.set(bounds);
+ if (insetBounds != null && !insetBounds.isEmpty()) {
+ mTmpRect.set(insetBounds);
+ setDisplayedBounds(bounds);
+ } else {
+ mTmpRect.set(bounds);
+ setDisplayedBounds(null);
+ }
adjustForMinimalTaskDimensions(mTmpRect);
setBounds(mTmpRect);
if (mStack == null || persistBounds) {
- setLastNonFullscreenBounds(getOverrideBounds());
+ setLastNonFullscreenBounds(getRequestedOverrideBounds());
}
- computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
+ computeOverrideConfiguration(newConfig, mTmpRect,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
- onOverrideConfigurationChanged(newConfig);
+ onRequestedOverrideConfigurationChanged(newConfig);
return !mTmpConfig.equals(newConfig);
}
@@ -1849,11 +1868,44 @@
mService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
}
+ /**
+ * 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);
+ }
+ final TaskWindowContainerController controller = getWindowContainerController();
+ if (controller != null && controller.mContainer != null) {
+ controller.mContainer.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();
+ }
+
/** 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,
+ void computeOverrideConfiguration(Configuration config, Rect bounds,
boolean overrideWidth, boolean overrideHeight) {
mTmpNonDecorBounds.set(bounds);
mTmpStableBounds.set(bounds);
@@ -1865,7 +1917,7 @@
if (mStack != null) {
final StackWindowController stackController = mStack.getWindowContainerController();
- stackController.adjustConfigurationForBounds(bounds, insetBounds,
+ stackController.adjustConfigurationForBounds(bounds,
mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
config, parentConfig, getWindowingMode());
} else {
@@ -1897,7 +1949,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;
}
@@ -1923,7 +1975,7 @@
mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
}
} else {
- updateOverrideConfiguration(inStack.getOverrideBounds());
+ updateOverrideConfiguration(inStack.getRequestedOverrideBounds());
}
}
@@ -1937,9 +1989,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;
}
@@ -1992,7 +2044,7 @@
? reuseActivitiesReport.base.intent.getComponent()
: null;
info.topActivity = reuseActivitiesReport.top != null
- ? reuseActivitiesReport.top.realActivity
+ ? reuseActivitiesReport.top.mActivityComponent
: null;
info.origActivity = origActivity;
info.realActivity = realActivity;
@@ -2043,7 +2095,7 @@
pw.println(origActivity.flattenToShortString());
}
if (realActivity != null) {
- pw.print(prefix); pw.print("realActivity=");
+ pw.print(prefix); pw.print("mActivityComponent=");
pw.println(realActivity.flattenToShortString());
}
if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) {
@@ -2158,7 +2210,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/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index a7b0272..9a56606 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -32,6 +32,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -65,6 +66,7 @@
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -141,6 +143,7 @@
final Rect taskBounds;
final Rect tmpContentInsets = new Rect();
final Rect tmpStableInsets = new Rect();
+ final InsetsState mTmpInsetsState = new InsetsState();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
int backgroundColor = WHITE;
int statusBarColor = 0;
@@ -201,7 +204,7 @@
try {
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
- tmpRect, tmpCutout, null);
+ tmpRect, tmpCutout, null, mTmpInsetsState);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return null;
@@ -217,7 +220,7 @@
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
- tmpCutout, tmpMergedConfiguration, surface);
+ tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
} catch (RemoteException e) {
// Local call.
}
@@ -312,7 +315,7 @@
// Keep a reference to it such that it doesn't get destroyed when finalized.
mChildSurfaceControl = new SurfaceControl.Builder(session)
.setName(mTitle + " - task-snapshot-surface")
- .setSize(buffer.getWidth(), buffer.getHeight())
+ .setBufferSize(buffer.getWidth(), buffer.getHeight())
.setFormat(buffer.getFormat())
.build();
Surface surface = new Surface();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 64f4ba5..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;
}
@@ -248,7 +254,6 @@
getRawBounds(mTmpRect);
final Rect stackBounds = getBounds();
getPendingTransaction()
- .setSize(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
.setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
.setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
mTmpRect.top - stackBounds.top);
@@ -277,7 +282,7 @@
@Override
public int setBounds(Rect bounds) {
- return setBounds(getOverrideBounds(), bounds);
+ return setBounds(getRequestedOverrideBounds(), bounds);
}
private int setBounds(Rect existing, Rect bounds) {
@@ -299,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());
}
@@ -443,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);
@@ -451,7 +457,7 @@
final int newDockSide = getDockSide(parentConfig, inOutBounds);
// Update the dock create mode and clear the dock create bounds, these
// might change after a rotation and the original values will be invalid.
- mService.setDockedStackCreateStateLocked(
+ mWmService.setDockedStackCreateStateLocked(
(newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
: SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
@@ -524,7 +530,7 @@
mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
displayCutout, outBounds);
final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
- mService.mContext.getResources(), displayWidth, displayHeight,
+ mWmService.mContext.getResources(), displayWidth, displayHeight,
dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
getDockSide(), isMinimizedDockAndHomeStackResizable());
final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
@@ -595,7 +601,7 @@
private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
boolean addingNew) {
final boolean canShowTask =
- showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
+ showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
final int stackSize = mChildren.size();
int minPosition = 0;
@@ -629,7 +635,7 @@
final Task tmpTask = mChildren.get(minPosition);
final boolean canShowTmpTask =
tmpTask.showForAllUsers()
- || mService.isCurrentProfileLocked(tmpTask.mUserId);
+ || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
if (canShowTmpTask) {
break;
}
@@ -648,7 +654,7 @@
final Task tmpTask = mChildren.get(maxPosition);
final boolean canShowTmpTask =
tmpTask.showForAllUsers()
- || mService.isCurrentProfileLocked(tmpTask.mUserId);
+ || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
if (!canShowTmpTask) {
break;
}
@@ -729,7 +735,7 @@
// We multiply by two to match the client logic for converting view elevation
// to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
- return (int)Math.ceil(mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+ return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
displayMetrics) * 2);
}
return 0;
@@ -740,7 +746,7 @@
return;
}
- final Rect stackBounds = getBounds();
+ final Rect stackBounds = getDisplayedBounds();
int width = stackBounds.width();
int height = stackBounds.height();
@@ -751,7 +757,6 @@
if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
return;
}
- transaction.setSize(mSurfaceControl, width, height);
transaction.setWindowCrop(mSurfaceControl, width, height);
mLastSurfaceSize.set(width, height);
}
@@ -793,7 +798,7 @@
if (dockedBounds == null || dockedBounds.isEmpty()) {
// Calculate the primary docked bounds.
- final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
+ final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
== SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(parentConfig,
true /* primary */, outStackBounds, dockedBounds,
@@ -858,8 +863,8 @@
outBounds.set(displayRect);
if (primary) {
- if (mService.mDockedStackCreateBounds != null) {
- outBounds.set(mService.mDockedStackCreateBounds);
+ if (mWmService.mDockedStackCreateBounds != null) {
+ outBounds.set(mWmService.mDockedStackCreateBounds);
return;
}
@@ -870,7 +875,7 @@
mDisplayContent.getDisplayPolicy().getStableInsetsLw(
parentConfig.windowConfiguration.getRotation(),
displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
- final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
+ final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
displayRect.width(),
displayRect.height(),
dockDividerWidth,
@@ -915,7 +920,7 @@
throw new IllegalStateException("Not a docked stack=" + this);
}
- mService.mDockedStackCreateBounds = null;
+ mWmService.mDockedStackCreateBounds = null;
final Rect bounds = new Rect();
final Rect tempBounds = new Rect();
@@ -957,7 +962,7 @@
}
mDisplayContent = null;
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
void resetAnimationBackgroundAnimator() {
@@ -979,7 +984,7 @@
int top = mChildren.size();
for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
Task task = mChildren.get(taskNdx);
- if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
+ if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
mChildren.remove(taskNdx);
mChildren.add(task);
--top;
@@ -1048,7 +1053,7 @@
}
mAdjustedForIme = false;
updateAdjustedBounds();
- mService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
+ mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
} else {
mImeGoingAway |= mAdjustedForIme;
}
@@ -1181,7 +1186,7 @@
}
if (dockSide == DOCKED_TOP) {
- mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+ mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
int topInset = mTmpRect.top;
mTmpAdjustedBounds.set(getRawBounds());
mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
@@ -1217,7 +1222,7 @@
}
if (dockSide == DOCKED_TOP) {
- mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+ mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
int topInset = mTmpRect.top;
return getRawBounds().bottom - topInset;
} else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
@@ -1242,11 +1247,11 @@
}
setAdjustedBounds(mTmpAdjustedBounds);
- final boolean isImeTarget = (mService.getImeFocusStackLocked() == this);
+ final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
if (mAdjustedForIme && adjust && !isImeTarget) {
final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
* IME_ADJUST_DIM_AMOUNT;
- mService.setResizeDimLayer(true, getWindowingMode(), alpha);
+ mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
}
}
@@ -1512,14 +1517,14 @@
public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (mCancelCurrentBoundsAnimation) {
return false;
}
}
try {
- mService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
+ mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
} catch (RemoteException e) {
// I don't believe you.
}
@@ -1537,7 +1542,7 @@
@Override // AnimatesBounds
public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (!isAttached()) {
// Don't run the animation if the stack is already detached
return false;
@@ -1557,7 +1562,7 @@
if (inPinnedWindowingMode()) {
try {
- mService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
+ mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
} catch (RemoteException e) {
// I don't believe you...
}
@@ -1600,9 +1605,9 @@
}
try {
- mService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
+ mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
if (moveToFullscreen) {
- mService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
+ mWmService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
true /* onTop */);
}
} catch (RemoteException e) {
@@ -1616,7 +1621,7 @@
@Override
public boolean isAttached() {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
return mDisplayContent != null;
}
}
@@ -1625,19 +1630,19 @@
* Called immediately prior to resizing the tasks at the end of the pinned stack animation.
*/
public void onPipAnimationEndResize() {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
mBoundsAnimating = false;
for (int i = 0; i < mChildren.size(); i++) {
final Task t = mChildren.get(i);
t.clearPreserveNonFloatingState();
}
- mService.requestTraversal();
+ mWmService.requestTraversal();
}
}
@Override
public boolean shouldDeferStartOnMoveToFullscreen() {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (!isAttached()) {
// Unnecessary to pause the animation because the stack is detached.
return false;
@@ -1756,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
index 59b2055..b87b65e 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,6 +16,14 @@
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;
@@ -24,18 +32,11 @@
import android.os.Message;
import android.util.EventLog;
import android.util.Slog;
+
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;
-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;
-
/**
* Controller for the task container. This is created by activity manager to link task records to
* the task container they use in window manager.
@@ -103,16 +104,15 @@
}
}
- public void positionChildAtTop(AppWindowContainerController childController) {
- positionChildAt(childController, POSITION_TOP);
+ void positionChildAtTop(AppWindowToken aToken) {
+ positionChildAt(aToken, POSITION_TOP);
}
- public void positionChildAt(AppWindowContainerController childController, int position) {
+ void positionChildAt(AppWindowToken aToken, int position) {
synchronized (mService.mGlobalLock) {
- final AppWindowToken aToken = childController.mContainer;
if (aToken == null) {
Slog.w(TAG_WM,
- "Attempted to position of non-existing app : " + childController);
+ "Attempted to position of non-existing app");
return;
}
@@ -157,7 +157,8 @@
throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found.");
}
- if (mContainer.setBounds(mContainer.getOverrideBounds(), forced) != BOUNDS_CHANGE_NONE
+ if (mContainer.setBounds(
+ mContainer.getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE
&& relayout) {
mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
diff --git a/services/core/java/com/android/server/wm/VrController.java b/services/core/java/com/android/server/wm/VrController.java
index abe40a7..3e136d35 100644
--- a/services/core/java/com/android/server/wm/VrController.java
+++ b/services/core/java/com/android/server/wm/VrController.java
@@ -187,7 +187,7 @@
synchronized (mGlobalAmLock) {
vrMode = record.requestedVrComponent != null;
requestedPackage = record.requestedVrComponent;
- userId = record.userId;
+ userId = record.mUserId;
callingPackage = record.info.getComponentName();
// Tell the VrController that a VR mode change is requested.
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 9216b66..e6ac059 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -20,19 +20,18 @@
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
-import android.graphics.Paint.FontMetricsInt;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Displays a watermark on top of the window manager's windows.
@@ -116,7 +115,7 @@
try {
ctrl = dc.makeOverlay()
.setName("WatermarkSurface")
- .setSize(1, 1)
+ .setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayerStack(mDisplay.getLayerStack());
@@ -133,7 +132,7 @@
if (mLastDW != dw || mLastDH != dh) {
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setSize(dw, dh);
+ mSurfaceControl.setBufferSize(dw, dh);
mDrawNeeded = true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 449c409..b8a0739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -260,8 +258,7 @@
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
- + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
+ + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 266006d..651089d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -115,7 +115,7 @@
*/
protected final Transaction mPendingTransaction;
protected final SurfaceAnimator mSurfaceAnimator;
- protected final WindowManagerService mService;
+ protected final WindowManagerService mWmService;
private final Point mTmpPos = new Point();
protected final Point mLastSurfacePosition = new Point();
@@ -129,10 +129,10 @@
*/
private boolean mCommittedReparentToAnimationLeash;
- WindowContainer(WindowManagerService service) {
- mService = service;
- mPendingTransaction = service.mTransactionFactory.make();
- mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
+ WindowContainer(WindowManagerService wms) {
+ mWmService = wms;
+ mPendingTransaction = wms.mTransactionFactory.make();
+ mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
}
@Override
@@ -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();
}
@@ -515,24 +516,6 @@
}
}
- /**
- * Update the surface size when display changed in order to avoid children being bound by the
- * old display size.
- *
- * Note that we don't want to apply this to all layers, but only limiting this to layers that
- * don't set their own size ({@link Task}, {@link WindowState} and {@link WindowToken}).
- */
- void updateSurfaceSize(DisplayContent dc) {
- if (mSurfaceControl == null) {
- return;
- }
-
- final int newSurfaceSize = dc.getSurfaceSize();
- if (mSurfaceControl.getWidth() != newSurfaceSize) {
- getPendingTransaction().setSize(mSurfaceControl, newSurfaceSize, newSurfaceSize);
- }
- }
-
void setWaitingForDrawnIfResizingChanged() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
@@ -1284,7 +1267,7 @@
return;
}
- getRelativePosition(mTmpPos);
+ getRelativeDisplayedPosition(mTmpPos);
if (mTmpPos.equals(mLastSurfacePosition)) {
return;
}
@@ -1293,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/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java
index 4b3cd36..3d3d2e0 100644
--- a/services/core/java/com/android/server/wm/WindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/WindowContainerListener.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.content.res.Configuration;
+
/**
* Interface used by the owner/creator of the container to listen to changes with the container.
* @see WindowContainerController
@@ -23,4 +25,5 @@
public interface WindowContainerListener {
void registerConfigurationChangeListener(ConfigurationContainerListener listener);
void unregisterConfigurationChangeListener(ConfigurationContainerListener listener);
+ default void onInitializeOverrideConfiguration(Configuration config) {}
}
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 39a8465..002d6d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -66,6 +66,7 @@
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
import static com.android.server.LockGuard.INDEX_WINDOW;
@@ -208,6 +209,7 @@
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
@@ -599,7 +601,6 @@
long mDisplayFreezeTime = 0;
int mLastDisplayFreezeDuration = 0;
Object mLastFinishedFreezeSource = null;
- boolean mWaitingForConfig = false;
boolean mSwitchingUser = false;
final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -734,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;
@@ -896,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;
}
@@ -922,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;
@@ -1112,7 +1114,8 @@
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+ DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+ InsetsState outInsetsState) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1460,6 +1463,7 @@
outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
}
+ outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -1857,7 +1861,7 @@
long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
- Surface outSurface) {
+ Surface outSurface, InsetsState outInsetsState) {
int result = 0;
boolean configChanged;
final boolean hasStatusBarPermission =
@@ -1870,7 +1874,7 @@
long origId = Binder.clearCallingIdentity();
final int displayId;
synchronized (mGlobalLock) {
- WindowState win = windowForClientLocked(session, client, false);
+ final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
@@ -1885,8 +1889,9 @@
win.setFrameNumber(frameNumber);
- if (!mWaitingForConfig) {
- win.finishSeamlessRotation();
+ final DisplayContent dc = win.getDisplayContent();
+ if (!dc.mWaitingForConfig) {
+ win.finishSeamlessRotation(false /* timeout */);
}
int attrChanges = 0;
@@ -2157,6 +2162,7 @@
outStableInsets, outOutsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
+ outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
if (localLOGV) Slog.v(
TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
@@ -2438,7 +2444,7 @@
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
displayContent.computeScreenConfiguration(mTempConfiguration);
if (currentConfig.diff(mTempConfiguration) != 0) {
- mWaitingForConfig = true;
+ displayContent.mWaitingForConfig = true;
displayContent.setLayoutNeeded();
int anim[] = new int[2];
displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
@@ -2451,9 +2457,10 @@
return config;
}
- void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
- if (mWaitingForConfig) {
- mWaitingForConfig = false;
+ void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+ @NonNull DisplayContent dc) {
+ if (dc.mWaitingForConfig) {
+ dc.mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
@@ -2474,26 +2481,36 @@
@Override
public void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
- boolean scaleUp) {
+ boolean scaleUp, int displayId) {
synchronized (mGlobalLock) {
- // TODO(multi-display): sysui using this api only support default display.
- mRoot.getDisplayContent(DEFAULT_DISPLAY)
- .mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.w(TAG, "Attempted to call overridePendingAppTransitionMultiThumbFuture"
+ + " for the display " + displayId + " that does not exist.");
+ return;
+ }
+ displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
callback, scaleUp);
}
}
@Override
- public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+ public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
+ int displayId) {
if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"overridePendingAppTransitionRemote()")) {
throw new SecurityException(
"Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
}
synchronized (mGlobalLock) {
- // TODO(multi-display): sysui using this api only support default display.
- mRoot.getDisplayContent(DEFAULT_DISPLAY)
- .mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.w(TAG, "Attempted to call overridePendingAppTransitionRemote"
+ + " for the display " + displayId + " that does not exist.");
+ return;
+ }
+ displayContent.mAppTransition.overridePendingAppTransitionRemote(
+ remoteAnimationAdapter);
}
}
@@ -3576,6 +3593,17 @@
}
}
+ void setRotateForApp(int displayId, boolean enabled) {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to set rotate for app for a missing display.");
+ return;
+ }
+ display.getDisplayRotation().setFixedToUserRotation(enabled);
+ }
+ }
+
@Override
public void freezeRotation(int rotation) {
freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
@@ -4181,13 +4209,11 @@
// placement to unfreeze the display since we froze it when the rotation was updated
// in DisplayContent#updateRotationUnchecked.
synchronized (mGlobalLock) {
- if (mWaitingForConfig) {
- mWaitingForConfig = false;
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc != null && dc.mWaitingForConfig) {
+ dc.mWaitingForConfig = false;
mLastFinishedFreezeSource = "config-unchanged";
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc != null) {
- dc.setLayoutNeeded();
- }
+ dc.setLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -5136,7 +5162,7 @@
configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
if (configChanged) {
- mWaitingForConfig = true;
+ displayContent.mWaitingForConfig = true;
startFreezingDisplayLocked(0 /* exitAnim */,
0 /* enterAnim */, displayContent);
displayContent.sendNewConfiguration();
@@ -5384,7 +5410,7 @@
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
+ displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
@@ -5396,23 +5422,24 @@
return;
}
- final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
- if (mWaitingForConfig || mAppsFreezingScreen > 0
+ final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+ final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+ final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+ if (waitingForConfig || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
- || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+ || mClientFreezingScreen || numOpeningApps > 0) {
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
- "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+ "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
+ ", mAppsFreezingScreen=" + mAppsFreezingScreen
+ ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
+ ", mClientFreezingScreen=" + mClientFreezingScreen
- + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+ + ", mOpeningApps.size()=" + numOpeningApps);
return;
}
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
"stopFreezingDisplayLocked: Unfreezing now");
- final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
// We must make a local copy of the displayId as it can be potentially overwritten later on
// in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -5615,22 +5642,12 @@
}
}
- // TODO: Make the callers use getNavBarPosition(int) only.
- /**
- * Used by SystemUI and shared SystemUI libraries.
- * @see DisplayPolicy#getNavBarPosition()
- */
- @Override
- @WindowManagerPolicy.NavigationBarPosition
- public int getNavBarPosition() {
- return getNavBarPosition(Display.DEFAULT_DISPLAY);
- }
-
/**
* Used by ActivityManager to determine where to position an app with aspect ratio shorter then
* the screen is.
* @see DisplayPolicy#getNavBarPosition()
*/
+ @Override
@WindowManagerPolicy.NavigationBarPosition
public int getNavBarPosition(int displayId) {
synchronized (mGlobalLock) {
@@ -5640,7 +5657,7 @@
if (displayContent == null) {
Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
+ " callers=" + Debug.getCallers(3));
- return -1;
+ return NAV_BAR_INVALID;
}
displayContent.performLayout(false /* initial */,
false /* updateInputWindows */);
@@ -5707,8 +5724,14 @@
}
@Override
- public boolean hasNavigationBar() {
- return mPolicy.hasNavigationBar();
+ public boolean hasNavigationBar(int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ return false;
+ }
+ return dc.getDisplayPolicy().hasNavigationBar();
+ }
}
@Override
@@ -6033,7 +6056,6 @@
pw.print(" windows="); pw.print(mWindowsFreezingScreen);
pw.print(" client="); pw.print(mClientFreezingScreen);
pw.print(" apps="); pw.print(mAppsFreezingScreen);
- pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
pw.print(" mRotation="); pw.print(defaultDisplayContent.getRotation());
pw.print(" mAltOrientation=");
@@ -6042,6 +6064,9 @@
pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
pw.print(" mLastOrientation=");
pw.println(defaultDisplayContent.getLastOrientation());
+ pw.print(" waitingForConfig=");
+ pw.println(defaultDisplayContent.mWaitingForConfig);
+
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
@@ -7257,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) {
@@ -7424,4 +7436,29 @@
}
}
}
+
+ @Override
+ public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException(
+ "Can't reparent display for non-existent displayId: " + displayId);
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int displayOwnerUid = display.getOwnerUid();
+ if (callingUid != displayOwnerUid) {
+ throw new SecurityException("Only owner of the display can reparent surfaces to it.");
+ }
+
+ synchronized (mGlobalLock) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+ displayContent.reparentDisplayContent(surfaceControlHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bf77ba8..6865ce3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -76,6 +76,8 @@
getNextArgRequired());
case "set-user-rotation":
return runSetDisplayUserRotation(pw);
+ case "set-fix-to-user-rotation":
+ return runSetFixToUserRotation(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -297,6 +299,32 @@
}
}
+ private int runSetFixToUserRotation(PrintWriter pw) {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean enabled;
+ switch (arg) {
+ case "enabled":
+ enabled = true;
+ break;
+ case "disabled":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting enabled or disabled, but we get "
+ + arg);
+ return -1;
+ }
+
+ mInternal.setRotateForApp(displayId, enabled);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -316,6 +344,8 @@
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
pw.println(" Set user rotation mode and user rotation.");
+ pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
+ pw.println(" Enable or disable rotating display for app requested orientation.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 484bd8c..f7f7528 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -443,7 +443,7 @@
for (int i = 0; i < activities.size(); i++) {
final ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
- r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+ r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
null, "finish-heavy", true);
}
}
@@ -513,7 +513,7 @@
}
ActivityRecord hist = mActivities.get(0);
intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
- intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId);
}
}
@@ -524,7 +524,7 @@
// Don't kill process(es) that has an activity not stopped.
return false;
}
- final TaskRecord otherTask = activity.getTask();
+ final TaskRecord otherTask = activity.getTaskRecord();
if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
// Don't kill process(es) that has an activity in a different task that is
// also in recents.
@@ -557,7 +557,7 @@
continue;
}
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task != null) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
+ " from " + r);
@@ -600,7 +600,7 @@
}
if (r.visible) {
callback.onVisibleActivity();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
@@ -755,9 +755,9 @@
return;
}
final ActivityDisplay activityDisplay =
- mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+ mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId);
if (activityDisplay != null) {
- mAtm.mStackSupervisor.getActivityDisplay(
+ mAtm.mRootActivityContainer.getActivityDisplay(
mDisplayId).unregisterConfigurationChangeListener(this);
}
mDisplayId = INVALID_DISPLAY;
@@ -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 567b583..d2dfa76 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -143,6 +143,7 @@
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -180,6 +181,7 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -577,10 +579,12 @@
*/
private boolean mIsDimming = false;
+ private @Nullable InsetsSourceProvider mInsetProvider;
+
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
- void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
- boolean requested) {
+ void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+ @Rotation int rotation, boolean requested) {
// Invisible windows and the wallpaper do not participate in the seamless rotation animation
if (!isVisibleNow() || mIsWallpaper) {
return;
@@ -593,16 +597,16 @@
if (mForceSeamlesslyRotate || requested) {
mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
mPendingSeamlessRotate.unrotate(transaction, this);
- mService.markForSeamlessRotation(this, true);
+ mWmService.markForSeamlessRotation(this, true);
}
}
- void finishSeamlessRotation() {
+ void finishSeamlessRotation(boolean timeout) {
if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.finish(this);
+ mPendingSeamlessRotate.finish(this, timeout);
mFinishSeamlessRotateFrameNumber = getFrameNumber();
mPendingSeamlessRotate = null;
- mService.markForSeamlessRotation(this, false);
+ mWmService.markForSeamlessRotation(this, false);
}
}
@@ -646,8 +650,8 @@
mAttrs.copyFrom(a);
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
mViewVisibility = viewVisibility;
- mPolicy = mService.mPolicy;
- mContext = mService.mContext;
+ mPolicy = mWmService.mPolicy;
+ mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
mSeq = seq;
mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
@@ -786,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
@@ -801,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
@@ -830,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
@@ -841,7 +848,7 @@
mWindowFrames.mContainingFrame.bottom =
mWindowFrames.mContainingFrame.top + frozen.height();
}
- final WindowState imeWin = mService.mRoot.getCurrentInputMethodWindow();
+ final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
@@ -880,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);
@@ -1199,7 +1201,7 @@
}
updateLastInsetValues();
- mService.makeWindowFreezingScreenIfNeededLocked(this);
+ mWmService.makeWindowFreezingScreenIfNeededLocked(this);
// If the orientation is changing, or we're starting or ending a drag resizing action,
// then we need to hold off on unfreezing the display until this window has been
@@ -1216,9 +1218,9 @@
mAppToken.clearAllDrawn();
}
}
- if (!mService.mResizingWindows.contains(this)) {
+ if (!mWmService.mResizingWindows.contains(this)) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
- mService.mResizingWindows.add(this);
+ mWmService.mResizingWindows.add(this);
}
} else if (getOrientationChanging()) {
if (isDrawnLw()) {
@@ -1226,7 +1228,7 @@
+ this + ", surfaceController " + winAnimator.mSurfaceController);
setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mService.mDisplayFreezeTime);
+ - mWmService.mDisplayFreezeTime);
}
}
}
@@ -1260,7 +1262,6 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- updateSurfaceSize(dc);
super.onDisplayChanged(dc);
// Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
if (dc != null) {
@@ -1600,7 +1601,7 @@
// 3. WS is currently visible
if (!runningAppAnimation && isVisibleNow) {
final AccessibilityController accessibilityController =
- mService.mAccessibilityController;
+ mWmService.mAccessibilityController;
final int winTransit = TRANSIT_EXIT;
mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
//TODO (multidisplay): Magnification is supported only for the default
@@ -1622,8 +1623,8 @@
if (isVisibleNow()) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mService.mAccessibilityController != null && isDefaultDisplay()) {
- mService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+ if (mWmService.mAccessibilityController != null && isDefaultDisplay()) {
+ mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
}
changed = true;
if (displayContent != null) {
@@ -1641,7 +1642,7 @@
@Override
void onResize() {
- final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+ final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) {
if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this);
resizingWindows.add(this);
@@ -1665,8 +1666,8 @@
mLayoutNeeded = true;
setDisplayLayoutNeeded();
- if (!mService.mResizingWindows.contains(this)) {
- mService.mResizingWindows.add(this);
+ if (!mWmService.mResizingWindows.contains(this)) {
+ mWmService.mResizingWindows.add(this);
}
}
@@ -1698,9 +1699,9 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null
+ if (mWmService.mAccessibilityController != null
&& getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
try {
@@ -1814,7 +1815,7 @@
// we are doing this as part of processing a death note.)
}
- mService.postWindowRemoveCleanupLocked(this);
+ mWmService.postWindowRemoveCleanupLocked(this);
}
@Override
@@ -1855,7 +1856,7 @@
+ " mWillReplaceWindow=" + mWillReplaceWindow
+ " inPendingTransaction="
+ (mAppToken != null ? mAppToken.inPendingTransaction : false)
- + " mDisplayFrozen=" + mService.mDisplayFrozen
+ + " mDisplayFrozen=" + mWmService.mDisplayFrozen
+ " callers=" + Debug.getCallers(6));
// Visibility of the removed window. Will be used later to update orientation later on.
@@ -1890,7 +1891,7 @@
mAppDied = true;
setDisplayLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
// Set up a replacement input channel since the app is now dead.
// We need to catch tapping on the dead window to restart the app.
@@ -1909,11 +1910,12 @@
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
// any change from that is performed immediately.
setDisplayLayoutNeeded();
- mService.requestTraversal();
+ mWmService.requestTraversal();
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ if (mWmService.mAccessibilityController != null
+ && displayId == DEFAULT_DISPLAY) {
+ mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
final boolean isAnimating = isAnimating()
@@ -1947,7 +1949,7 @@
displayContent.sendNewConfiguration();
}
}
- mService.updateFocusedWindowLocked(isFocused()
+ mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
@@ -1961,9 +1963,9 @@
setDisplayLayoutNeeded();
// Request a focus update as this window's input channel is already gone. Otherwise
// we could have no focused window in input manager.
- final boolean focusChanged = mService.updateFocusedWindowLocked(
+ final boolean focusChanged = mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
if (focusChanged) {
getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
}
@@ -2016,7 +2018,7 @@
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
- super(inputChannel, mService.mH.getLooper());
+ super(inputChannel, mWmService.mH.getLooper());
}
@Override
public void onInputEvent(InputEvent event) {
@@ -2036,7 +2038,7 @@
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
- mInputWindowHandle.inputChannel = inputChannels[0];
+ mInputWindowHandle.token = mClient.asBinder();
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
@@ -2047,7 +2049,7 @@
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
- mService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
+ mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}
void disposeInputChannel() {
@@ -2058,7 +2060,7 @@
// unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
- mService.mInputManager.unregisterInputChannel(mInputChannel);
+ mWmService.mInputManager.unregisterInputChannel(mInputChannel);
mInputChannel.dispose();
mInputChannel = null;
@@ -2067,7 +2069,7 @@
mClientChannel.dispose();
mClientChannel = null;
}
- mInputWindowHandle.inputChannel = null;
+ mInputWindowHandle.token = null;
}
/** Returns true if the replacement window was removed. */
@@ -2141,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.
@@ -2166,11 +2168,12 @@
mTmpRect.inset(-delta, -delta);
}
region.set(mTmpRect);
- cropRegionToStackBoundsIfNeeded(region);
+ region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
} else {
// Not modal or full screen modal
- getTouchableRegion(region);
+ getTouchableRegion(region, true /* forSurface */);
}
+
return flags;
}
@@ -2186,13 +2189,13 @@
if (isFocused()) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"setAnimationLocked: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
+ mWmService.mFocusMayChange = true;
}
setDisplayLayoutNeeded();
// Window is no longer visible -- make sure if we were waiting
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
}
}
}
@@ -2209,8 +2212,8 @@
// We need to turn on screen regardless of visibility.
boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
boolean allowTheaterMode =
- mService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
- mService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
+ mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
+ mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
== 0;
boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
@@ -2260,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(mService.mProcessConfigurations.get(
- pid, mService.mRoot.getConfiguration()));
+ final Configuration processConfig =
+ mWmService.mAtmService.getGlobalConfigurationForPid(pid);
+ mTempConfiguration.setTo(processConfig == null
+ ? mWmService.mRoot.getConfiguration() : processConfig);
return mTempConfiguration;
}
@@ -2317,13 +2322,14 @@
public void binderDied() {
try {
boolean resetSplitScreenResizing = false;
- synchronized (mService.mGlobalLock) {
- final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
+ synchronized (mWmService.mGlobalLock) {
+ final WindowState win = mWmService
+ .windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
final DisplayContent dc = getDisplayContent();
if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
- mService.mTaskSnapshotController.onAppDied(win.mAppToken);
+ mWmService.mTaskSnapshotController.onAppDied(win.mAppToken);
}
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
@@ -2347,7 +2353,7 @@
try {
// Note: this calls into ActivityManager, so we must *not* hold the window
// manager lock while calling this.
- mService.mActivityTaskManager.setSplitScreenResizing(false);
+ mWmService.mActivityTaskManager.setSplitScreenResizing(false);
} catch (RemoteException e) {
// Local call, shouldn't return RemoteException.
throw e.rethrowAsRuntimeException();
@@ -2391,11 +2397,11 @@
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mAppToken == null || mAppToken.windowsAreFocusable())
- && !canReceiveTouchInput();
+ && !cantReceiveTouchInput();
}
- /** @return true if this window desires touch events. */
- boolean canReceiveTouchInput() {
+ /** @return false if this window desires touch events. */
+ boolean cantReceiveTouchInput() {
return mAppToken != null && mAppToken.getTask() != null
&& mAppToken.getTask().mStack.shouldIgnoreInput();
}
@@ -2454,10 +2460,10 @@
mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
}
return true;
}
@@ -2492,18 +2498,18 @@
// Window is no longer visible -- make sure if we were waiting
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
if (isFocused) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"WindowState.hideLw: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
+ mWmService.mFocusMayChange = true;
}
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
if (isFocused) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
}
return true;
}
@@ -2565,7 +2571,7 @@
// and add the window only if the permission was granted. Therefore, if
// the mode is MODE_DEFAULT we want the op to succeed as the window is
// shown.
- final int mode = mService.mAppOps.startOpNoThrow(mAppOp,
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
getOwningUid(), getOwningPackage(), true);
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
setAppOpVisibilityLw(false);
@@ -2574,7 +2580,7 @@
void resetAppOpsState() {
if (mAppOp != OP_NONE && mAppOpVisibility) {
- mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+ mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
}
}
@@ -2587,13 +2593,13 @@
if (mAppOpVisibility) {
// There is a race between the check and the finish calls but this is fine
// as this would mean we will get another change callback and will reconcile.
- int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
+ int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
- mService.mAppOps.finishOp(mAppOp, uid, packageName);
+ mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
setAppOpVisibilityLw(false);
}
} else {
- final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
setAppOpVisibilityLw(true);
}
@@ -2614,7 +2620,7 @@
// in wake lock statistics. So in particular, we don't want to include the
// window's hash code as in toString().
final CharSequence tag = getWindowTag();
- mDrawLock = mService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
+ mDrawLock = mWmService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
mDrawLock.setReferenceCounted(false);
mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
}
@@ -2699,10 +2705,10 @@
mAppFreezing = false;
if (mHasSurface && !getOrientationChanging()
- && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+ && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
setOrientationChanging(true);
- mService.mRoot.mOrientationChangeComplete = false;
+ mWmService.mRoot.mOrientationChangeComplete = false;
}
mLastFreezeDuration = 0;
setDisplayLayoutNeeded();
@@ -2792,7 +2798,7 @@
}
return win.mShowToOwnerOnly
- && !mService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
+ && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
}
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
@@ -2801,7 +2807,18 @@
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;
+ }
+
final Rect frame = mWindowFrames.mFrame;
switch (mTouchableInsets) {
default:
@@ -2816,11 +2833,18 @@
break;
case TOUCHABLE_INSETS_REGION: {
outRegion.set(mGivenTouchableRegion);
- outRegion.translate(frame.left, frame.top);
break;
}
}
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) {
@@ -2884,7 +2908,7 @@
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
- new MergedConfiguration(mService.mRoot.getConfiguration(),
+ new MergedConfiguration(mWmService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
setLastReportedMergedConfiguration(mergedConfiguration);
@@ -2905,7 +2929,7 @@
if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& mClient instanceof IWindow.Stub) {
// To prevent deadlock simulate one-way call if win.mClient is a local object.
- mService.mH.post(new Runnable() {
+ mWmService.mH.post(new Runnable() {
@Override
public void run() {
try {
@@ -2924,8 +2948,8 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
mWindowFrames.resetInsetsChanged();
@@ -2934,16 +2958,39 @@
} catch (RemoteException e) {
setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mService.mDisplayFreezeTime);
+ - mWmService.mDisplayFreezeTime);
// We are assuming the hosting process is dead or in a zombie state.
Slog.w(TAG, "Failed to report 'resized' to the client of " + this
+ ", removing this window.");
- mService.mPendingRemove.add(this);
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mPendingRemove.add(this);
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ /**
+ * Called when the insets state changed.
+ */
+ void notifyInsetsChanged() {
+ try {
+ mClient.insetsChanged(
+ getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset state change", e);
+ }
+ }
+
+ void notifyInsetsControlChanged() {
+ final InsetsStateController stateController =
+ getDisplayContent().getInsetsStateController();
+ try {
+ mClient.insetsControlChanged(stateController.getInsetsForDispatch(this),
+ stateController.getControlsForDispatch(this));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset state change", e);
+ }
+ }
+
Rect getBackdropFrame(Rect frame) {
// When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
// start even if we haven't received the relayout window, so that the client requests
@@ -2988,7 +3035,7 @@
}
public void registerFocusObserver(IWindowFocusObserver observer) {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (mFocusCallbacks == null) {
mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>();
}
@@ -2997,7 +3044,7 @@
}
public void unregisterFocusObserver(IWindowFocusObserver observer) {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (mFocusCallbacks != null) {
mFocusCallbacks.unregister(observer);
}
@@ -3070,7 +3117,7 @@
@Override
void setWaitingForDrawnIfResizingChanged() {
if (isDragResizeChanged()) {
- mService.mWaitingForDrawn.add(this);
+ mWmService.mWaitingForDrawn.add(this);
}
super.setWaitingForDrawnIfResizingChanged();
}
@@ -3559,7 +3606,7 @@
if (dc != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
dc.setLayoutNeeded();
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -3657,7 +3704,7 @@
logPerformShow("Showing ");
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
mWinAnimator.applyEnterAnimationLocked();
// Force the show in the next prepareSurfaceLocked() call.
@@ -3665,7 +3712,7 @@
if (DEBUG_ANIM) Slog.v(TAG,
"performShowLocked: mDrawState=HAS_DRAWN in " + this);
mWinAnimator.mDrawState = HAS_DRAWN;
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
if (mHidden) {
mHidden = false;
@@ -3944,7 +3991,7 @@
if (mWinAnimator.mEnteringAnimation) {
mWinAnimator.mEnteringAnimation = false;
- mService.requestTraversal();
+ mWmService.requestTraversal();
// System windows don't have an activity and an app token as a result, but need a way
// to be informed about their entrance animation end.
if (mAppToken == null) {
@@ -3960,8 +4007,8 @@
}
//TODO (multidisplay): Accessibility is supported only for the default display.
- if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
if (!isSelfOrAncestorWindowAnimatingExit()) {
@@ -3987,10 +4034,10 @@
mAppToken.destroySurfaces();
} else {
if (hasSurface) {
- mService.mDestroySurface.add(this);
+ mWmService.mDestroySurface.add(this);
}
if (mRemoveOnExit) {
- mService.mPendingRemove.add(this);
+ mWmService.mPendingRemove.add(this);
mRemoveOnExit = false;
}
}
@@ -4021,7 +4068,7 @@
}
if (mDestroying) {
mDestroying = false;
- mService.mDestroySurface.remove(this);
+ mWmService.mDestroySurface.remove(this);
didSomething = true;
}
}
@@ -4050,7 +4097,7 @@
if (displayContent != null) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
displayContent.pendingLayoutChanges);
}
}
@@ -4265,7 +4312,7 @@
}
if (mDestroying) {
mDestroying = false;
- mService.mDestroySurface.remove(this);
+ mWmService.mDestroySurface.remove(this);
}
if (oldVisibility == View.GONE) {
mWinAnimator.mEnterAnimationPending = true;
@@ -4333,10 +4380,10 @@
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
+ anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */),
- mService.mSurfaceAnimationRunner);
+ mWmService.mSurfaceAnimationRunner);
startAnimation(mPendingTransaction, adapter);
commitPendingTransaction();
}
@@ -4350,7 +4397,7 @@
transformFrameToSurfacePosition(left, top, newPosition);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
- mService.mSurfaceAnimationRunner);
+ mWmService.mSurfaceAnimationRunner);
startAnimation(getPendingTransaction(), adapter);
}
@@ -4444,7 +4491,7 @@
public boolean isFocused() {
final WindowState outer = mOuter.get();
if (outer != null) {
- synchronized (outer.mService.mGlobalLock) {
+ synchronized (outer.mWmService.mGlobalLock) {
return outer.isFocused();
}
}
@@ -4575,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);
}
@@ -4765,6 +4812,14 @@
mWindowFrames.setContentChanged(false);
}
+ void setInsetProvider(InsetsSourceProvider insetProvider) {
+ mInsetProvider = insetProvider;
+ }
+
+ InsetsSourceProvider getInsetProvider() {
+ return mInsetProvider;
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
@@ -4776,7 +4831,7 @@
final Animation anim = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.window_move_from_decor);
mDuration = (long)
- (anim.computeDurationHint() * mService.getWindowAnimationScaleLocked());
+ (anim.computeDurationHint() * mWmService.getWindowAnimationScaleLocked());
mInterpolator = anim.getInterpolator();
mFrom.set(fromX, fromY);
mTo.set(toX, toY);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e090cc5..fb5c556 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -234,7 +234,7 @@
private final Point mTmpPos = new Point();
WindowStateAnimator(final WindowState win) {
- final WindowManagerService service = win.mService;
+ final WindowManagerService service = win.mWmService;
mService = service;
mAnimator = service.mAnimator;
@@ -861,7 +861,7 @@
// to find the surface size changed underneath it.
final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
if (relayout) {
- mSurfaceResized = mSurfaceController.setSizeInTransaction(
+ mSurfaceResized = mSurfaceController.setBufferSizeInTransaction(
mTmpSize.width(), mTmpSize.height(), recoveringMemory);
} else {
mSurfaceResized = false;
@@ -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/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6821e94..ce627e2 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -19,34 +19,28 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.IBinder;
import android.os.Debug;
+import android.os.IBinder;
import android.os.Trace;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowContentFrameStats;
-import android.view.Surface.OutOfResourcesException;
-import android.util.Slog;
-
-import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
class WindowSurfaceController {
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfaceController" : TAG_WM;
@@ -106,7 +100,7 @@
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl())
.setName(name)
- .setSize(w, h)
+ .setBufferSize(w, h)
.setFormat(format)
.setFlags(flags)
.setMetadata(windowType, ownerUid);
@@ -303,7 +297,7 @@
}
}
- boolean setSizeInTransaction(int width, int height, boolean recoveringMemory) {
+ boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
if (surfaceResized) {
mSurfaceW = width;
@@ -312,7 +306,7 @@
try {
if (SHOW_TRANSACTIONS) logSurface(
"SIZE " + width + "x" + height, null);
- mSurfaceControl.setSize(width, height);
+ mSurfaceControl.setBufferSize(width, height);
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
// as running out of memory), don't take down the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7193dd7..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -113,7 +113,9 @@
return;
}
- if (mService.mWaitingForConfig) {
+ // TODO(multi-display):
+ final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+ if (defaultDisplay.mWaitingForConfig) {
// Our configuration has changed (most likely rotation), but we
// don't yet have the complete configuration to report to
// applications. Don't do any window layout until we have it.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 0cf79b6..9c13782 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -172,8 +172,8 @@
setHidden(true);
if (changed) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
}
if (delayed) {
@@ -202,7 +202,7 @@
if (!mChildren.contains(win)) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
addChild(win, mWindowComparator);
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
// TODO: Should we also be setting layout needed here and other places?
}
}
@@ -265,7 +265,6 @@
// to another display before the window behind
// it is ready.
- updateSurfaceSize(dc);
super.onDisplayChanged(dc);
}
@@ -327,9 +326,9 @@
* system bars, or in other words extend outside of the "Decor Frame"
*/
boolean canLayerAboveSystemBars() {
- int layer = mService.mPolicy.getWindowLayerFromTypeLw(windowType,
+ int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
mOwnerCanManageAppTokens);
- int navLayer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
+ int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
mOwnerCanManageAppTokens);
return mOwnerCanManageAppTokens && (layer > navLayer);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index bf83ac13..b85489a 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",
@@ -108,6 +106,7 @@
"android.hardware.contexthub@1.0",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
+ "android.hardware.gnss@2.0",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
"android.hardware.power@1.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ee8a08b..43d2dcf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -32,6 +32,7 @@
#include <atomic>
#include <cinttypes>
#include <limits.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
@@ -71,6 +72,7 @@
#define INDENT " "
+using android::base::ParseUint;
using android::base::StringPrintf;
namespace android {
@@ -81,11 +83,13 @@
static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
static struct {
+ jclass clazz;
jmethodID notifyConfigurationChanged;
jmethodID notifyInputDevicesChanged;
jmethodID notifySwitch;
jmethodID notifyInputChannelBroken;
jmethodID notifyANR;
+ jmethodID notifyFocusChanged;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -94,6 +98,7 @@
jmethodID checkInjectEventsPermission;
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
+ jmethodID getInputPortAssociations;
jmethodID getKeyRepeatTimeout;
jmethodID getKeyRepeatDelay;
jmethodID getHoverTapTimeout;
@@ -147,15 +152,6 @@
return value ? "true" : "false";
}
-static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
- const sp<InputApplicationHandle>& inputApplicationHandle) {
- if (inputApplicationHandle == nullptr) {
- return nullptr;
- }
- return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
- getInputApplicationHandleObjLocalRef(env);
-}
-
static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
status_t status = android_view_PointerIcon_loadSystemIcon(env,
@@ -191,6 +187,13 @@
WM_ACTION_PASS_TO_USER = 1,
};
+static std::string getStringElementFromJavaArray(JNIEnv* env, jobjectArray array, jsize index) {
+ jstring item = jstring(env->GetObjectArrayElement(array, index));
+ ScopedUtfChars chars(env, item);
+ std::string result(chars.c_str());
+ return result;
+}
+
// --- NativeInputManager ---
@@ -249,6 +252,7 @@
const sp<IBinder>& token,
const std::string& reason);
virtual void notifyInputChannelBroken(const sp<IBinder>& token);
+ virtual void notifyFocusChanged(const sp<IBinder>& token);
virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
@@ -342,6 +346,8 @@
mInteractive = true;
mInputManager = new InputManager(this, this);
+ defaultServiceManager()->addService(String16("inputflinger"),
+ mInputManager, false);
}
NativeInputManager::~NativeInputManager() {
@@ -457,20 +463,44 @@
}
outConfig->excludedDeviceNames.clear();
- jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mServiceObj,
- gServiceClassInfo.getExcludedDeviceNames));
+ jobjectArray excludedDeviceNames = jobjectArray(env->CallStaticObjectMethod(
+ gServiceClassInfo.clazz, gServiceClassInfo.getExcludedDeviceNames));
if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) {
jsize length = env->GetArrayLength(excludedDeviceNames);
for (jsize i = 0; i < length; i++) {
- jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
- const char* deviceNameChars = env->GetStringUTFChars(item, nullptr);
- outConfig->excludedDeviceNames.push_back(deviceNameChars);
- env->ReleaseStringUTFChars(item, deviceNameChars);
- env->DeleteLocalRef(item);
+ std::string deviceName = getStringElementFromJavaArray(env, excludedDeviceNames, i);
+ outConfig->excludedDeviceNames.push_back(deviceName);
}
env->DeleteLocalRef(excludedDeviceNames);
}
+ // Associations between input ports and display ports
+ // The java method packs the information in the following manner:
+ // Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}]
+ // Received data: ['inputPort1', '1', 'inputPort2', '2']
+ // So we unpack accordingly here.
+ outConfig->portAssociations.clear();
+ jobjectArray portAssociations = jobjectArray(env->CallStaticObjectMethod(
+ gServiceClassInfo.clazz, gServiceClassInfo.getInputPortAssociations));
+ if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) {
+ jsize length = env->GetArrayLength(portAssociations);
+ for (jsize i = 0; i < length / 2; i++) {
+ std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i);
+ std::string displayPortStr =
+ getStringElementFromJavaArray(env, portAssociations, 2 * i + 1);
+ uint8_t displayPort;
+ // Should already have been validated earlier, but do it here for safety.
+ bool success = ParseUint(displayPortStr, &displayPort);
+ if (!success) {
+ ALOGE("Could not parse entry in port configuration file, received: %s",
+ displayPortStr.c_str());
+ continue;
+ }
+ outConfig->portAssociations.insert({inputPort, displayPort});
+ }
+ env->DeleteLocalRef(portAssociations);
+ }
+
jint hoverTapTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getHoverTapTimeout);
if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
@@ -656,13 +686,11 @@
JNIEnv* env = jniEnv();
- jobject inputApplicationHandleObj =
- getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
jobject tokenObj = javaObjectForIBinder(env, token);
jstring reasonObj = env->NewStringUTF(reason.c_str());
jlong newTimeout = env->CallLongMethod(mServiceObj,
- gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
+ gServiceClassInfo.notifyANR, tokenObj,
reasonObj);
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; // abort dispatch
@@ -671,7 +699,6 @@
}
env->DeleteLocalRef(reasonObj);
- env->DeleteLocalRef(inputApplicationHandleObj);
return newTimeout;
}
@@ -691,6 +718,22 @@
}
}
+void NativeInputManager::notifyFocusChanged(const sp<IBinder>& token) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ ALOGD("notifyFocusChanged");
+#endif
+ ATRACE_CALL();
+
+ JNIEnv* env = jniEnv();
+
+ jobject tokenObj = javaObjectForIBinder(env, token);
+ if (tokenObj) {
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
+ tokenObj);
+ checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
+ }
+}
+
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -1689,6 +1732,10 @@
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find method " methodName);
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! (var), "Unable to find static method " methodName);
+
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
@@ -1703,6 +1750,7 @@
jclass clazz;
FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
+ gServiceClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz,
"notifyConfigurationChanged", "(J)V");
@@ -1715,10 +1763,13 @@
GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
"notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
+
+ GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
+ "notifyFocusChanged", "(Landroid/os/IBinder;)V");
GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
"notifyANR",
- "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
+ "(Landroid/os/IBinder;Ljava/lang/String;)J");
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
@@ -1743,9 +1794,12 @@
GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
- GET_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
+ GET_STATIC_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
+ GET_STATIC_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz,
+ "getInputPortAssociations", "()[Ljava/lang/String;");
+
GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz,
"getKeyRepeatTimeout", "()I");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9216005..4d0556c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -20,9 +20,11 @@
#include <android/hardware/gnss/1.0/IGnss.h>
#include <android/hardware/gnss/1.1/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
#include <android/hardware/gnss/1.0/IGnssMeasurement.h>
#include <android/hardware/gnss/1.1/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.0/IGnssMeasurement.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include "hardware_legacy/power.h"
@@ -110,13 +112,15 @@
using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
+using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
+using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
-
+using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
@@ -135,6 +139,7 @@
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss_V1_0> gnssHal = nullptr;
sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
+sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil> agnssRilIface = nullptr;
sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
@@ -146,6 +151,7 @@
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
+sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr;
sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
#define WAKE_LOCK_NAME "GPS"
@@ -744,7 +750,9 @@
* GnssMeasurementCallback implements the callback methods required for the
* GnssMeasurement interface.
*/
-struct GnssMeasurementCallback : public IGnssMeasurementCallback_V1_1 {
+struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 {
+ Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_V2_0::GnssData& data)
+ override;
Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override;
Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
private:
@@ -761,6 +769,11 @@
void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
};
+Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
+ const IGnssMeasurementCallback_V2_0::GnssData& data) {
+ // TODO(b/119571122): implement gnssMeasurementCb_2_0
+ return Void();
+}
Return<void> GnssMeasurementCallback::gnssMeasurementCb(
const IGnssMeasurementCallback_V1_1::GnssData& data) {
@@ -1126,13 +1139,22 @@
}
static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
- gnssHal_V1_1 = IGnss_V1_1::getService();
- if (gnssHal_V1_1 == nullptr) {
- ALOGD("gnssHal 1.1 was null, trying 1.0");
- gnssHal = IGnss_V1_0::getService();
- } else {
- gnssHal = gnssHal_V1_1;
+ gnssHal_V2_0 = IGnss_V2_0::getService();
+ if (gnssHal_V2_0 != nullptr) {
+ gnssHal = gnssHal_V2_0;
+ gnssHal_V1_1 = gnssHal_V2_0;
+ return;
}
+
+ ALOGD("gnssHal 2.0 was null, trying 1.1");
+ gnssHal_V1_1 = IGnss_V1_1::getService();
+ if (gnssHal_V1_1 != nullptr) {
+ gnssHal = gnssHal_V1_1;
+ return;
+ }
+
+ ALOGD("gnssHal 1.1 was null, trying 1.0");
+ gnssHal = IGnss_V1_0::getService();
}
static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) {
@@ -1187,110 +1209,120 @@
LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
}
- if (gnssHal != nullptr) {
- gnssHalDeathRecipient = new GnssDeathRecipient();
- hardware::Return<bool> linked = gnssHal->linkToDeath(
- gnssHalDeathRecipient, /*cookie*/ 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to GnssHAL death: %s",
- linked.description().c_str());
- } else if (!linked) {
- ALOGW("Unable to link to GnssHal death notifications");
- } else {
- ALOGD("Link to death notification successful");
- }
+ if (gnssHal == nullptr) {
+ ALOGE("Unable to get GPS service\n");
+ return;
+ }
- auto gnssXtra = gnssHal->getExtensionXtra();
- if (!gnssXtra.isOk()) {
- ALOGD("Unable to get a handle to Xtra");
- } else {
- gnssXtraIface = gnssXtra;
- }
+ gnssHalDeathRecipient = new GnssDeathRecipient();
+ hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to GnssHAL death: %s",
+ linked.description().c_str());
+ } else if (!linked) {
+ ALOGW("Unable to link to GnssHal death notifications");
+ } else {
+ ALOGD("Link to death notification successful");
+ }
- auto gnssRil = gnssHal->getExtensionAGnssRil();
- if (!gnssRil.isOk()) {
- ALOGD("Unable to get a handle to AGnssRil");
- } else {
- agnssRilIface = gnssRil;
- }
+ auto gnssXtra = gnssHal->getExtensionXtra();
+ if (!gnssXtra.isOk()) {
+ ALOGD("Unable to get a handle to Xtra");
+ } else {
+ gnssXtraIface = gnssXtra;
+ }
- auto gnssAgnss = gnssHal->getExtensionAGnss();
- if (!gnssAgnss.isOk()) {
- ALOGD("Unable to get a handle to AGnss");
- } else {
- agnssIface = gnssAgnss;
- }
+ auto gnssRil = gnssHal->getExtensionAGnssRil();
+ if (!gnssRil.isOk()) {
+ ALOGD("Unable to get a handle to AGnssRil");
+ } else {
+ agnssRilIface = gnssRil;
+ }
- auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
- if (!gnssNavigationMessage.isOk()) {
- ALOGD("Unable to get a handle to GnssNavigationMessage");
- } else {
- gnssNavigationMessageIface = gnssNavigationMessage;
- }
+ auto gnssAgnss = gnssHal->getExtensionAGnss();
+ if (!gnssAgnss.isOk()) {
+ ALOGD("Unable to get a handle to AGnss");
+ } else {
+ agnssIface = gnssAgnss;
+ }
- if (gnssHal_V1_1 != nullptr) {
- auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
- if (!gnssMeasurement.isOk()) {
- ALOGD("Unable to get a handle to GnssMeasurement");
- } else {
- gnssMeasurementIface_V1_1 = gnssMeasurement;
- gnssMeasurementIface = gnssMeasurementIface_V1_1;
- }
- } else {
- auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
- if (!gnssMeasurement_V1_0.isOk()) {
- ALOGD("Unable to get a handle to GnssMeasurement");
- } else {
- gnssMeasurementIface = gnssMeasurement_V1_0;
- }
- }
+ auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+ if (!gnssNavigationMessage.isOk()) {
+ ALOGD("Unable to get a handle to GnssNavigationMessage");
+ } else {
+ gnssNavigationMessageIface = gnssNavigationMessage;
+ }
- auto gnssDebug = gnssHal->getExtensionGnssDebug();
- if (!gnssDebug.isOk()) {
- ALOGD("Unable to get a handle to GnssDebug");
+ if (gnssHal_V2_0 != nullptr) {
+ // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
+ auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+ if (!gnssMeasurement.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
} else {
- gnssDebugIface = gnssDebug;
+ gnssMeasurementIface_V2_0 = gnssMeasurement;
+ gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
+ gnssMeasurementIface = gnssMeasurementIface_V2_0;
}
+ } else if (gnssHal_V1_1 != nullptr) {
+ auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
+ if (!gnssMeasurement.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement_V1_1");
+ } else {
+ gnssMeasurementIface_V1_1 = gnssMeasurement;
+ gnssMeasurementIface = gnssMeasurementIface_V1_1;
+ }
+ } else {
+ auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
+ if (!gnssMeasurement_V1_0.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement");
+ } else {
+ gnssMeasurementIface = gnssMeasurement_V1_0;
+ }
+ }
- auto gnssNi = gnssHal->getExtensionGnssNi();
- if (!gnssNi.isOk()) {
- ALOGD("Unable to get a handle to GnssNi");
- } else {
- gnssNiIface = gnssNi;
- }
+ auto gnssDebug = gnssHal->getExtensionGnssDebug();
+ if (!gnssDebug.isOk()) {
+ ALOGD("Unable to get a handle to GnssDebug");
+ } else {
+ gnssDebugIface = gnssDebug;
+ }
- if (gnssHal_V1_1 != nullptr) {
- auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
- if (!gnssConfiguration.isOk()) {
- ALOGD("Unable to get a handle to GnssConfiguration");
- } else {
- gnssConfigurationIface_V1_1 = gnssConfiguration;
- gnssConfigurationIface = gnssConfigurationIface_V1_1;
- }
- } else {
- auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
- if (!gnssConfiguration_V1_0.isOk()) {
- ALOGD("Unable to get a handle to GnssConfiguration");
- } else {
- gnssConfigurationIface = gnssConfiguration_V1_0;
- }
- }
+ auto gnssNi = gnssHal->getExtensionGnssNi();
+ if (!gnssNi.isOk()) {
+ ALOGD("Unable to get a handle to GnssNi");
+ } else {
+ gnssNiIface = gnssNi;
+ }
- auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
- if (!gnssGeofencing.isOk()) {
- ALOGD("Unable to get a handle to GnssGeofencing");
+ if (gnssHal_V1_1 != nullptr) {
+ auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
+ if (!gnssConfiguration.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration");
} else {
- gnssGeofencingIface = gnssGeofencing;
- }
-
- auto gnssBatching = gnssHal->getExtensionGnssBatching();
- if (!gnssBatching.isOk()) {
- ALOGD("Unable to get a handle to gnssBatching");
- } else {
- gnssBatchingIface = gnssBatching;
+ gnssConfigurationIface_V1_1 = gnssConfiguration;
+ gnssConfigurationIface = gnssConfigurationIface_V1_1;
}
} else {
- ALOGE("Unable to get GPS service\n");
+ auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
+ if (!gnssConfiguration_V1_0.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration");
+ } else {
+ gnssConfigurationIface = gnssConfiguration_V1_0;
+ }
+ }
+
+ auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+ if (!gnssGeofencing.isOk()) {
+ ALOGD("Unable to get a handle to GnssGeofencing");
+ } else {
+ gnssGeofencingIface = gnssGeofencing;
+ }
+
+ auto gnssBatching = gnssHal->getExtensionGnssBatching();
+ if (!gnssBatching.isOk()) {
+ ALOGD("Unable to get a handle to gnssBatching");
+ } else {
+ gnssBatchingIface = gnssBatching;
}
}
@@ -1820,10 +1852,11 @@
sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback();
IGnssMeasurement_V1_0::GnssMeasurementStatus result =
- IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;;
- if (gnssMeasurementIface_V1_1 != nullptr) {
- result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface,
- enableFullTracking);
+ IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;
+ if (gnssMeasurementIface_V2_0 != nullptr) {
+ result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking);
+ } else if (gnssMeasurementIface_V1_1 != nullptr) {
+ result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, enableFullTracking);
} else {
if (enableFullTracking == JNI_TRUE) {
// full tracking mode not supported in 1.0 HAL
@@ -1837,7 +1870,7 @@
static_cast<int32_t>(result));
return JNI_FALSE;
} else {
- ALOGD("gnss measurement infc has been enabled");
+ ALOGD("gnss measurement infc has been enabled");
}
return JNI_TRUE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
new file mode 100644
index 0000000..05912a5
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager.InstallUpdateCallback;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.UpdateEngine;
+import android.os.UpdateEngineCallback;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+/**
+ * Used for installing an update on <a href="https://source.android.com/devices/tech/ota/ab">AB
+ * devices.</a>
+ * <p>This logic is specific to GOTA and should be modified by OEMs using a different AB update
+ * system.</p>
+ */
+class AbUpdateInstaller extends UpdateInstaller {
+ private static final String PAYLOAD_BIN = "payload.bin";
+ private static final String PAYLOAD_PROPERTIES_TXT = "payload_properties.txt";
+ //https://en.wikipedia.org/wiki/Zip_(file_format)#Local_file_header
+ private static final int OFFSET_TO_FILE_NAME = 30;
+ // kDownloadStateInitializationError constant from system/update_engine/common/error_code.h.
+ private static final int DOWNLOAD_STATE_INITIALIZATION_ERROR = 20;
+ private long mSizeForUpdate;
+ private long mOffsetForUpdate;
+ private List<String> mProperties;
+ private Enumeration<? extends ZipEntry> mEntries;
+ private ZipFile mPackedUpdateFile;
+ private static final Map<Integer, Integer> errorCodesMap = buildErrorCodesMap();
+ private static final Map<Integer, String> errorStringsMap = buildErrorStringsMap();
+ public static final String UNKNOWN_ERROR = "Unknown error with error code = ";
+ private boolean mUpdateInstalled;
+
+ private static Map<Integer, Integer> buildErrorCodesMap() {
+ Map<Integer, Integer> map = new HashMap<>();
+ map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ DOWNLOAD_STATE_INITIALIZATION_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+
+ // Error constants corresponding to errors related to bad update file.
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+
+ // Error constants corresponding to errors related to devices bad state.
+ map.put(
+ UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+
+ return map;
+ }
+
+ private static Map<Integer, String> buildErrorStringsMap() {
+ Map<Integer, String> map = new HashMap<>();
+ map.put(UpdateEngine.ErrorCodeConstants.ERROR, UNKNOWN_ERROR);
+ map.put(
+ DOWNLOAD_STATE_INITIALIZATION_ERROR,
+ "The delta update payload was targeted for another version or the source partition"
+ + "was modified after it was installed");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+ "Failed to finish the configured postinstall works.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+ "Failed to open one of the partitions it tried to write to or read data from.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+ "Payload mismatch error.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+ "Failed to read the payload data from the given URL.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, "Payload hash error.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+ "Payload size mismatch error.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+ "Failed to verify the signature of the payload.");
+ map.put(
+ UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+ "The payload has been successfully installed,"
+ + "but the active slot was not flipped.");
+ return map;
+ }
+
+ AbUpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+ StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+ DevicePolicyConstants constants) {
+ super(context, updateFileDescriptor, callback, injector, constants);
+ mUpdateInstalled = false;
+ }
+
+ @Override
+ public void installUpdateInThread() {
+ if (mUpdateInstalled) {
+ throw new IllegalStateException("installUpdateInThread can be called only once.");
+ }
+ try {
+ setState();
+ applyPayload(Paths.get(mCopiedUpdateFile.getAbsolutePath()).toUri().toString());
+ } catch (ZipException e) {
+ Log.w(UpdateInstaller.TAG, e);
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ Log.getStackTraceString(e));
+ } catch (IOException e) {
+ Log.w(UpdateInstaller.TAG, e);
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e));
+ }
+ }
+
+ private void setState() throws IOException {
+ mUpdateInstalled = true;
+ mPackedUpdateFile = new ZipFile(mCopiedUpdateFile);
+ mProperties = new ArrayList<>();
+ mSizeForUpdate = -1;
+ mOffsetForUpdate = 0;
+ mEntries = mPackedUpdateFile.entries();
+ }
+
+ private UpdateEngine buildBoundUpdateEngine() {
+ UpdateEngine updateEngine = new UpdateEngine();
+ updateEngine.bind(new DelegatingUpdateEngineCallback(this, updateEngine));
+ return updateEngine;
+ }
+
+ private void applyPayload(String updatePath) throws IOException {
+ if (!updateStateForPayload()) {
+ return;
+ }
+ String[] headerKeyValuePairs = mProperties.stream().toArray(String[]::new);
+ if (mSizeForUpdate == -1) {
+ Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package.");
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ "Failed to find payload entry in the given package.");
+ return;
+ }
+
+ UpdateEngine updateEngine = buildBoundUpdateEngine();
+ updateEngine.applyPayload(
+ updatePath, mOffsetForUpdate, mSizeForUpdate, headerKeyValuePairs);
+ }
+
+ private boolean updateStateForPayload() throws IOException {
+ long offset = 0;
+ while (mEntries.hasMoreElements()) {
+ ZipEntry entry = mEntries.nextElement();
+
+ String name = entry.getName();
+ offset += buildOffsetForEntry(entry, name);
+ if (entry.isDirectory()) {
+ offset -= entry.getCompressedSize();
+ continue;
+ }
+ if (PAYLOAD_BIN.equals(name)) {
+ if (entry.getMethod() != ZipEntry.STORED) {
+ Log.w(UpdateInstaller.TAG, "Invalid compression method.");
+ notifyCallbackOnError(
+ InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+ "Invalid compression method.");
+ return false;
+ }
+ mSizeForUpdate = entry.getCompressedSize();
+ mOffsetForUpdate = offset - entry.getCompressedSize();
+ } else if (PAYLOAD_PROPERTIES_TXT.equals(name)) {
+ updatePropertiesForEntry(entry);
+ }
+ }
+ return true;
+ }
+
+ private long buildOffsetForEntry(ZipEntry entry, String name) {
+ return OFFSET_TO_FILE_NAME + name.length() + entry.getCompressedSize()
+ + (entry.getExtra() == null ? 0 : entry.getExtra().length);
+ }
+
+ private void updatePropertiesForEntry(ZipEntry entry) throws IOException {
+ try (BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(mPackedUpdateFile.getInputStream(entry)))) {
+ String line;
+ /* Neither @line nor @mProperties are size constraint since there is a few properties
+ with limited size. */
+ while ((line = bufferedReader.readLine()) != null) {
+ mProperties.add(line);
+ }
+ }
+ }
+
+ private static class DelegatingUpdateEngineCallback extends UpdateEngineCallback {
+ private UpdateInstaller mUpdateInstaller;
+ private UpdateEngine mUpdateEngine;
+
+ DelegatingUpdateEngineCallback(
+ UpdateInstaller updateInstaller, UpdateEngine updateEngine) {
+ mUpdateInstaller = updateInstaller;
+ mUpdateEngine = updateEngine;
+ }
+
+ @Override
+ public void onStatusUpdate(int statusCode, float percentage) {
+ return;
+ }
+
+ @Override
+ public void onPayloadApplicationComplete(int errorCode) {
+ mUpdateEngine.unbind();
+ if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) {
+ mUpdateInstaller.notifyCallbackOnSuccess();
+ } else {
+ mUpdateInstaller.notifyCallbackOnError(
+ errorCodesMap.getOrDefault(
+ errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN),
+ errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode));
+ }
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 5926bdd..65d3245 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,10 +17,15 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
import android.content.ComponentName;
+import android.os.ParcelFileDescriptor;
import com.android.server.SystemService;
+import java.util.Collections;
+import java.util.List;
+
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -91,4 +96,33 @@
@Override
public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
+
+ @Override
+ public void installUpdateFromFile(ComponentName admin,
+ ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
+
+ @Override
+ public void addCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+ }
+
+ @Override
+ public boolean removeCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+ return false;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackages(ComponentName admin) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+ int userHandle) {
+ return false;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+ return Collections.emptyList();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 71fea02..fd59b43 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -42,6 +42,12 @@
private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
"das_died_service_stable_connection_threshold_sec";
+ private static final String BATTERY_THRESHOLD_NOT_CHARGING_KEY =
+ "battery_threshold_not_charging";
+
+ private static final String BATTERY_THRESHOLD_CHARGING_KEY =
+ "battery_threshold_charging";
+
/**
* The back-off before re-connecting, when a service binding died, due to the owner
* crashing repeatedly.
@@ -63,6 +69,17 @@
*/
public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+ /**
+ * Battery threshold for installing system update while the device is not charging.
+ */
+ public final int BATTERY_THRESHOLD_NOT_CHARGING;
+
+ /**
+ * Battery threshold for installing system update while the device is charging.
+ */
+ public final int BATTERY_THRESHOLD_CHARGING;
+
+
private DevicePolicyConstants(String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
@@ -87,6 +104,12 @@
DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
TimeUnit.MINUTES.toSeconds(2));
+ int batteryThresholdNotCharging = parser.getInt(
+ BATTERY_THRESHOLD_NOT_CHARGING_KEY, 40);
+
+ int batteryThresholdCharging = parser.getInt(
+ BATTERY_THRESHOLD_CHARGING_KEY, 20);
+
// Set minimum: 5 seconds.
dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
@@ -103,6 +126,8 @@
DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
dasDiedServiceStableConnectionThresholdSec;
+ BATTERY_THRESHOLD_NOT_CHARGING = batteryThresholdNotCharging;
+ BATTERY_THRESHOLD_CHARGING = batteryThresholdCharging;
}
public static DevicePolicyConstants loadFromString(String settings) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bbbc40c..bca3b1f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -74,8 +74,10 @@
.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .ADMIN_TYPE_DEVICE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -113,6 +115,7 @@
import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
@@ -379,6 +382,8 @@
private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
private static final Set<Integer> DA_DISALLOWED_POLICIES;
+ private static final String AB_DEVICE_KEY = "ro.build.ab_update";
+
static {
SECURE_SETTINGS_WHITELIST = new ArraySet<>();
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -905,8 +910,12 @@
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
- private static final String TAG_METERED_DATA_DISABLED_PACKAGES
- = "metered_data_disabled_packages";
+ private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
+ "metered_data_disabled_packages";
+ private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
+ "cross-profile-calendar-packages";
+ private static final String TAG_PACKAGE = "package";
+
DeviceAdminInfo info;
@@ -1027,6 +1036,9 @@
String startUserSessionMessage = null;
String endUserSessionMessage = null;
+ // The whitelist of packages that can access cross profile calendar APIs.
+ final Set<String> mCrossProfileCalendarPackages = new ArraySet<>();
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
isParent = parent;
@@ -1296,6 +1308,12 @@
out.text(endUserSessionMessage);
out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
}
+ if (!mCrossProfileCalendarPackages.isEmpty()) {
+ out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+ writeAttributeValuesToXml(
+ out, TAG_PACKAGE, mCrossProfileCalendarPackages);
+ out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1488,6 +1506,9 @@
} else {
Log.w(LOG_TAG, "Missing text when loading end session message");
}
+ } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
+ readAttributeValues(
+ parser, TAG_PACKAGE, mCrossProfileCalendarPackages);
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1703,6 +1724,8 @@
pw.print(prefix); pw.println("parentAdmin:");
parentAdmin.dump(prefix + " ", pw);
}
+ pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
+ pw.println(mCrossProfileCalendarPackages);
}
}
@@ -4867,14 +4890,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"
@@ -4961,7 +4986,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);
}
@@ -4970,7 +4995,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) {
@@ -13315,4 +13340,113 @@
return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
}
+
+ @Override
+ public void installUpdateFromFile(ComponentName admin,
+ ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
+ enforceDeviceOwner(admin);
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ UpdateInstaller updateInstaller;
+ if (isDeviceAB()) {
+ updateInstaller = new AbUpdateInstaller(
+ mContext, updateFileDescriptor, callback, mInjector, mConstants);
+ } else {
+ updateInstaller = new NonAbUpdateInstaller(
+ mContext, updateFileDescriptor, callback, mInjector, mConstants);
+ }
+ updateInstaller.startInstallUpdate();
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ private boolean isDeviceAB() {
+ return "true".equalsIgnoreCase(android.os.SystemProperties
+ .get(AB_DEVICE_KEY, ""));
+ }
+
+ @Override
+ public void addCrossProfileCalendarPackage(ComponentName who, String packageName) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (admin.mCrossProfileCalendarPackages.add(packageName)) {
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+ }
+ }
+
+ @Override
+ public boolean removeCrossProfileCalendarPackage(ComponentName who, String packageName) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+ boolean isRemoved = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ isRemoved = admin.mCrossProfileCalendarPackages.remove(packageName);
+ if (isRemoved) {
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+ }
+ return isRemoved;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackages(ComponentName who) {
+ if (!mHasFeature) {
+ return Collections.emptyList();
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+ }
+ }
+
+ @Override
+ public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+ int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+ enforceCrossUsersPermission(userHandle);
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+ 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();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
new file mode 100644
index 0000000..5f1e926
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.RecoverySystem;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Used for installing an update for <a href="https://source.android.com/devices/tech/ota/nonab">non
+ * AB</a> devices.
+ */
+class NonAbUpdateInstaller extends UpdateInstaller {
+ NonAbUpdateInstaller(Context context,
+ ParcelFileDescriptor updateFileDescriptor,
+ StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+ DevicePolicyConstants constants) {
+ super(context, updateFileDescriptor, callback, injector, constants);
+ }
+
+ @Override
+ public void installUpdateInThread() {
+ try {
+ RecoverySystem.installPackage(mContext, mCopiedUpdateFile);
+ notifyCallbackOnSuccess();
+ } catch (IOException e) {
+ Log.w(TAG, "IO error while trying to install non AB update.", e);
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
new file mode 100644
index 0000000..7910598
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+abstract class UpdateInstaller {
+ private StartInstallingUpdateCallback mCallback;
+ private ParcelFileDescriptor mUpdateFileDescriptor;
+ private DevicePolicyConstants mConstants;
+ protected Context mContext;
+ protected File mCopiedUpdateFile;
+
+ static final String TAG = "UpdateInstaller";
+ private DevicePolicyManagerService.Injector mInjector;
+
+ protected UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+ StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+ DevicePolicyConstants constants) {
+ mContext = context;
+ mCallback = callback;
+ mUpdateFileDescriptor = updateFileDescriptor;
+ mInjector = injector;
+ mConstants = constants;
+ }
+
+ public abstract void installUpdateInThread();
+
+ public void startInstallUpdate() {
+ if (!checkIfBatteryIsSufficient()) {
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
+ "The battery level must be above "
+ + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
+ + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
+ return;
+ }
+ Thread thread = new Thread(() -> {
+ mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir();
+ if (mCopiedUpdateFile == null) {
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ "Error while copying file.");
+ return;
+ }
+ installUpdateInThread();
+ });
+ thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ }
+
+ private boolean checkIfBatteryIsSufficient() {
+ BatteryManager batteryManager =
+ (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
+ if (batteryManager != null) {
+ int chargePercentage = batteryManager
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ return batteryManager.isCharging()
+ ? chargePercentage >= mConstants.BATTERY_THRESHOLD_CHARGING
+ : chargePercentage >= mConstants.BATTERY_THRESHOLD_NOT_CHARGING;
+ }
+ return false;
+ }
+
+ private File copyUpdateFileToDataOtaPackageDir() {
+ try {
+ File destination = createNewFileWithPermissions();
+ copyToFile(destination);
+ return destination;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to copy update file to OTA directory", e);
+ notifyCallbackOnError(
+ DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+ Log.getStackTraceString(e));
+ return null;
+ }
+ }
+
+ private File createNewFileWithPermissions() throws IOException {
+ File destination = File.createTempFile(
+ "update", ".zip", new File(Environment.getDataDirectory() + "/ota_package"));
+ FileUtils.setPermissions(
+ /* path= */ destination,
+ /* mode= */ FileUtils.S_IRWXU | FileUtils.S_IRGRP | FileUtils.S_IROTH,
+ /* uid= */ -1, /* gid= */ -1);
+ return destination;
+ }
+
+ private void copyToFile(File destination) throws IOException {
+ try (OutputStream out = new FileOutputStream(destination);
+ InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+ mUpdateFileDescriptor)) {
+ FileUtils.copy(in, out);
+ }
+ }
+
+ void cleanupUpdateFile() {
+ if (mCopiedUpdateFile.exists()) {
+ mCopiedUpdateFile.delete();
+ }
+ }
+
+ protected void notifyCallbackOnError(int errorCode, String errorMessage) {
+ cleanupUpdateFile();
+ try {
+ mCallback.onStartInstallingUpdateError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while calling callback", e);
+ }
+ }
+
+ protected void notifyCallbackOnSuccess() {
+ cleanupUpdateFile();
+ mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
+ }
+}
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 a7f45ee..0000000
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ /dev/null
@@ -1,190 +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.INTELLIGENCE_MANAGER_SERVICE;
-
-import android.annotation.NonNull;
-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.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.
- */
-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 "intel_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(INTELLIGENCE_MANAGER_SERVICE,
- new IntelligenceManagerServiceStub());
- publishLocalService(IntelligenceManagerInternal.class, mLocalService);
- }
-
- @Override // from AbstractMasterSystemService
- protected void onServiceRemoved(@NonNull IntelligencePerUserService service,
- @UserIdInt int userId) {
- service.destroyLocked();
- }
-
- /**
- * 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
- */
- private boolean sendActivityAssistDataLocked(@UserIdInt int userId,
- @NonNull IBinder activityToken, @NonNull Bundle data) {
- final IntelligencePerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- return service.sendActivityAssistDataLocked(activityToken, data);
- }
- return false;
- }
-
- 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(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(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(int userId, @NonNull InteractionSessionId sessionId) {
- Preconditions.checkNotNull(sessionId);
-
- synchronized (mLock) {
- final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.finishSessionLocked(sessionId);
- }
- }
-
- @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, 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) {
- return sendActivityAssistDataLocked(userId, activityToken, data);
- }
- }
- }
-}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8ac41f..2baef6af 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;
@@ -36,7 +37,6 @@
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.DisplayManagerInternal;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -56,6 +56,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;
@@ -121,6 +124,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;
@@ -235,8 +239,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 =
@@ -655,7 +659,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;
@@ -717,17 +721,9 @@
// Manages Overlay packages
traceBeginAndSlog("StartOverlayManagerService");
- OverlayManagerService overlayManagerService = new OverlayManagerService(
- mSystemContext, installer);
- mSystemServiceManager.startService(overlayManagerService);
+ mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
traceEnd();
- if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
- // DisplayManager needs the overlay immediately.
- overlayManagerService.updateSystemUiContext();
- LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
- }
-
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked
@@ -805,10 +801,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",
@@ -934,7 +926,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,
@@ -1003,6 +995,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);
@@ -1139,13 +1136,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");
@@ -1407,6 +1398,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)
@@ -1417,15 +1417,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 {
@@ -1512,6 +1503,14 @@
}
traceEnd();
+ traceBeginAndSlog("RuntimeService");
+ try {
+ ServiceManager.addService("runtime", new RuntimeService(context));
+ } catch (Throwable e) {
+ reportWtf("starting RuntimeService", e);
+ }
+ traceEnd();
+
// timezone.RulesManagerService will prevent a device starting up if the chain of trust
// required for safe time zone updates might be broken. RuleManagerService cannot do
// this check when mOnlyCore == true, so we don't enable the service in this case.
@@ -2101,6 +2100,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/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index dc55179..c9b9f3e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -146,13 +146,14 @@
final long identity = Binder.clearCallingIdentity();
try {
disabledMessage = dpmi.getPrintingDisabledReasonForUser(callingUserId);
+
+ if (disabledMessage != null) {
+ Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
+ Toast.LENGTH_LONG).show();
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (disabledMessage != null) {
- Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
- Toast.LENGTH_LONG).show();
- }
try {
adapter.start();
} catch (RemoteException re) {
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 565152c..6f10ed5 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,6 +26,7 @@
LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
+ bmgrlib \
services.backup \
services.core \
services.net
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
new file mode 100644
index 0000000..1705f5b
--- /dev/null
+++ b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.bmgr;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/** Unit tests for {@link com.android.commands.bmgr.Bmgr}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BmgrTest {
+ @Mock private IBackupManager mBackupManager;
+ private Bmgr mBmgr;
+
+ /** Common setup run before each test method. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mBmgr = new Bmgr(mBackupManager);
+ }
+
+ /**
+ * Test that bmgr uses the default user {@link UserHandle.USER_SYSTEM} if no user is specified.
+ */
+ @Test
+ public void testRun_whenUserNotSpecified_callsBackupManagerAsSystemUser() throws Exception {
+ mBmgr.run(new String[] {"run"});
+
+ verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM);
+ }
+
+ /** Test that bmgr uses the specified user if an user is specified. */
+ @Test
+ public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception {
+ mBmgr.run(new String[] {"--user", "10"});
+
+ verify(mBackupManager).isBackupServiceActive(10);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index f7bb68c..ba4caf44 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,49 +16,27 @@
package com.android.server.backup;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
import static com.android.server.backup.testing.TransportData.backupTransport;
-import static com.android.server.backup.testing.TransportData.d2dTransport;
-import static com.android.server.backup.testing.TransportData.localTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-import android.app.backup.BackupManager;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
-import com.android.server.backup.testing.TransportTestUtils.TransportMock;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.testing.shadows.ShadowAppBackupUtils;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
-import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,977 +44,482 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowSettings;
import java.io.File;
-import java.util.List;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
-/** Tests for the system service {@link BackupManagerService} that performs backup/restore. */
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
@Presubmit
public class BackupManagerServiceTest {
- private static final String TAG = "BMSTest";
- private static final String PACKAGE_1 = "some.package.1";
- private static final String PACKAGE_2 = "some.package.2";
+ private static final String TEST_PACKAGE = "package";
+ private static final String TEST_TRANSPORT = "transport";
+ @Mock private UserBackupManagerService mUserBackupManagerService;
@Mock private TransportManager mTransportManager;
- private HandlerThread mBackupThread;
- private ShadowLooper mShadowBackupLooper;
- private File mBaseStateDir;
- private File mDataDir;
- private ShadowContextWrapper mShadowContext;
+ private BackupManagerService mBackupManagerService;
private Context mContext;
- private TransportData mTransport;
- private String mTransportName;
- private ShadowPackageManager mShadowPackageManager;
- /**
- * Initialize state that {@link BackupManagerService} operations interact with. This includes
- * setting up the transport, starting the backup thread, and creating backup data directories.
- */
+ /** Initialize {@link BackupManagerService}. */
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTransport = backupTransport();
- mTransportName = mTransport.transportName;
-
- // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
- // we should not fail tests because of this. This is not flakiness, the exceptions thrown
- // don't interfere with the tests.
- mBackupThread = startSilentBackupThread(TAG);
- mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
-
- ContextWrapper context = RuntimeEnvironment.application;
- mShadowPackageManager = shadowOf(context.getPackageManager());
- mContext = context;
- mShadowContext = shadowOf(context);
-
- File cacheDir = mContext.getCacheDir();
- // Corresponds to /data/backup
- mBaseStateDir = new File(cacheDir, "base_state");
- // Corresponds to /cache/backup_stage
- mDataDir = new File(cacheDir, "data");
+ Application application = RuntimeEnvironment.application;
+ mContext = application;
+ mBackupManagerService =
+ new BackupManagerService(
+ application,
+ new Trampoline(application),
+ BackupManagerServiceTestUtils.startBackupThread(null),
+ new File(application.getCacheDir(), "base_state"),
+ new File(application.getCacheDir(), "data"),
+ mTransportManager);
+ mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
}
/**
- * Clean up and reset state that was created for testing {@link BackupManagerService}
- * operations.
- */
- @After
- public void tearDown() throws Exception {
- mBackupThread.quit();
- ShadowAppBackupUtils.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
- * specifically to prevent overloading the logs in production.
+ * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
+ * This is specifically to prevent overloading the logs in production.
*/
@Test
- public void testMoreDebug_isFalse() {
+ public void testMoreDebug_isFalse() throws Exception {
boolean moreDebug = BackupManagerService.MORE_DEBUG;
assertThat(moreDebug).isFalse();
}
- /**
- * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns the
- * current destination string of inputted transport if the transport is registered.
- */
+ // TODO(b/118520567): Change the following tests to use the per-user instance of
+ // UserBackupManagerService once it's implemented. Currently these tests only test the straight
+ // forward redirection.
+
+ // ---------------------------------------------
+ // Backup agent tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDestinationString() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
- .thenReturn("destinationString");
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testDataChanged_callsDataChangedForUser() throws Exception {
+ mBackupManagerService.dataChanged(TEST_PACKAGE);
- String destination = backupManagerService.getDestinationString(mTransportName);
-
- assertThat(destination).isEqualTo("destinationString");
+ verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns {@code
- * null} if the inputted transport is not registered.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDestinationString_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
- .thenThrow(TransportNotRegisteredException.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
+ IBinder agentBinder = mock(IBinder.class);
- String destination = backupManagerService.getDestinationString(mTransportName);
+ mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
- assertThat(destination).isNull();
+ verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
}
- /**
- * Test verifying that {@link BackupManagerService#getDestinationString(String)} throws a {@link
- * SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDestinationString_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
- .thenThrow(TransportNotRegisteredException.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
+ mBackupManagerService.agentDisconnected(TEST_PACKAGE);
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.getDestinationString(mTransportName));
+ verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
- * {@code false} when the given app is not eligible for backup.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testOpComplete_callsOpCompleteForUser() throws Exception {
+ mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
- boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
-
- assertThat(result).isFalse();
+ verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
}
- /**
- * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
- * {@code true} when the given app is eligible for backup.
- */
+ // ---------------------------------------------
+ // Transport tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
+ String[] transports = {TEST_TRANSPORT};
- boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+ mBackupManagerService.initializeTransports(transports, /* observer */ null);
- assertThat(result).isTrue();
- verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
}
- /**
- * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} throws a
- * {@link SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
+ mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+ verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
- * returns an {@code array} of only apps that are eligible for backup from an {@array} of
- * inputted apps.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
+ mBackupManagerService.getCurrentTransport();
- String[] filtered =
- backupManagerService.filterAppsEligibleForBackup(
- new String[] {PACKAGE_1, PACKAGE_2});
-
- assertThat(filtered).asList().containsExactly(PACKAGE_1);
- verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ verify(mUserBackupManagerService).getCurrentTransport();
}
- /**
- * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
- * returns an empty {@code array} if no inputted apps are eligible for backup.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
+ throws Exception {
+ mBackupManagerService.getCurrentTransportComponent();
- String[] filtered =
- backupManagerService.filterAppsEligibleForBackup(
- new String[] {PACKAGE_1, PACKAGE_2});
-
- assertThat(filtered).isEmpty();
+ verify(mUserBackupManagerService).getCurrentTransportComponent();
}
- /**
- * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])} throws
- * a {@link SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
+ mBackupManagerService.listAllTransports();
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.filterAppsEligibleForBackup(
- new String[] {PACKAGE_1, PACKAGE_2}));
+ verify(mUserBackupManagerService).listAllTransports();
}
- /* Tests for select transport */
-
- private ComponentName mNewTransportComponent;
- private TransportData mNewTransport;
- private TransportMock mNewTransportMock;
- private TransportData mOldTransport;
- private TransportMock mOldTransportMock;
-
- private void setUpForSelectTransport() throws Exception {
- mNewTransport = backupTransport();
- mNewTransportComponent = mNewTransport.getTransportComponent();
- mOldTransport = d2dTransport();
- List<TransportMock> transportMocks =
- setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
- mNewTransportMock = transportMocks.get(0);
- mOldTransportMock = transportMocks.get(1);
- when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
- .thenReturn(mOldTransport.transportName);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} successfully
- * switches the current transport to the inputted transport, returns the name of the old
- * transport, and disposes of the transport client after the operation.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransport() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
+ throws Exception {
+ mBackupManagerService.listAllTransportComponents();
- String oldTransport =
- backupManagerService.selectBackupTransport(mNewTransport.transportName);
-
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
- verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ verify(mUserBackupManagerService).listAllTransportComponents();
}
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} throws a
- * {@link SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransport_withoutPermission() throws Exception {
- setUpForSelectTransport();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
+ mBackupManagerService.getTransportWhitelist();
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+ verify(mUserBackupManagerService).getTransportWhitelist();
}
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
- * transport and disposes of the transport client after the operation.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransportAsync() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- verify(callback).onSuccess(eq(mNewTransport.transportName));
- verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
- * transport and notifies the inputted callback of failure when it fails to register the
- * transport.
- */
- @Test
- public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
- verify(callback).onFailure(anyInt());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
- * transport and notifies the inputted callback of failure when the transport gets unregistered.
- */
- @Test
- public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
- setUpTransports(mTransportManager, mTransport.unregistered());
- ComponentName newTransportComponent = mTransport.getTransportComponent();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
- verify(callback).onFailure(anyInt());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
- * have backup permission.
- */
- @Test
- public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
- setUpForSelectTransport();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ComponentName newTransportComponent = mNewTransport.getTransportComponent();
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.selectBackupTransportAsync(
- newTransportComponent, mock(ISelectBackupTransportCallback.class)));
- }
-
- private String getSettingsTransport() {
- return ShadowSettings.ShadowSecure.getString(
- mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns the
- * {@link ComponentName} of the currently selected transport.
- */
- @Test
- public void testGetCurrentTransportComponent() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getCurrentTransportComponent())
- .thenReturn(mTransport.getTransportComponent());
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
- assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
- * {@code null} if there is no currently selected transport.
- */
- @Test
- public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
- assertThat(transportComponent).isNull();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
- * {@code null} if the currently selected transport is not registered.
- */
- @Test
- public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getCurrentTransportComponent())
- .thenThrow(TransportNotRegisteredException.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
- assertThat(transportComponent).isNull();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} throws a
- * {@link SecurityException} if the caller does not have backup permission.
- */
- @Test
- public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
- }
-
- /* Tests for updating transport attributes */
-
- private static final int PACKAGE_UID = 10;
- private ComponentName mTransportComponent;
- private int mTransportUid;
-
- private void setUpForUpdateTransportAttributes() throws Exception {
- mTransportComponent = mTransport.getTransportComponent();
- String transportPackage = mTransportComponent.getPackageName();
-
- ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
- shadowPackageManager.addPackage(transportPackage);
- shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
-
- mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} succeeds if the uid of the transport is same as the
- * uid of the caller.
- */
- @Test
- public void
- testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
+ throws Exception {
+ TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
+ mBackupManagerService.updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
configurationIntent,
"currentDestinationString",
dataManagementIntent,
"dataManagementLabel");
- verify(mTransportManager)
+ verify(mUserBackupManagerService)
.updateTransportAttributes(
- eq(mTransportComponent),
- eq(mTransportName),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(dataManagementIntent),
- eq("dataManagementLabel"));
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the uid of the
- * transport is not equal to the uid of the caller.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+ public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
+ mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ mBackupManagerService.selectBackupTransportAsync(
+ transport.getTransportComponent(), callback);
+
+ verify(mUserBackupManagerService)
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
+ mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
+ mBackupManagerService.getDestinationString(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
+ mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
+ mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ // ---------------------------------------------
+ // Settings tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
+ mBackupManagerService.setBackupEnabled(true);
+
+ verify(mUserBackupManagerService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
+ mBackupManagerService.setAutoRestore(true);
+
+ verify(mUserBackupManagerService).setAutoRestore(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
+ mBackupManagerService.setBackupProvisioned(true);
+
+ verify(mUserBackupManagerService).setBackupProvisioned(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
+ mBackupManagerService.isBackupEnabled();
+
+ verify(mUserBackupManagerService).isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // Backup tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
+ mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+ verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ String[] packages = {TEST_PACKAGE};
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid + 1,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
+ mBackupManagerService.filterAppsEligibleForBackup(packages);
+
+ verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
- * null} transport component.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+ public void testBackupNow_callsBackupNowForUser() throws Exception {
+ mBackupManagerService.backupNow();
+
+ verify(mUserBackupManagerService).backupNow();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
+ mBackupManagerService.cancelBackups();
+
+ verify(mUserBackupManagerService).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
+ FullBackupJob job = new FullBackupJob();
+
+ mBackupManagerService.beginFullBackup(job);
+
+ verify(mUserBackupManagerService).beginFullBackup(job);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
+ mBackupManagerService.endFullBackup();
+
+ verify(mUserBackupManagerService).endFullBackup();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
+ String[] packages = {TEST_PACKAGE};
+
+ mBackupManagerService.fullTransportBackup(packages);
+
+ verify(mUserBackupManagerService).fullTransportBackup(packages);
+ }
+
+ // ---------------------------------------------
+ // Restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
+ mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
+ mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- null,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
+ verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
- * null} transport name.
- */
- @Test
- public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ // ---------------------------------------------
+ // Adb backup/restore tests
+ // ---------------------------------------------
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- null,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
+ mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
- * null} destination string.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+ public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
+ mBackupManagerService.hasBackupPassword();
+
+ verify(mUserBackupManagerService).hasBackupPassword();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbBackup_callsAdbBackupForUser() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ ParcelFileDescriptor parcelFileDescriptor =
+ ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ String[] packages = {TEST_PACKAGE};
+
+ mBackupManagerService.adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ packages);
+
+ verify(mUserBackupManagerService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ packages);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ ParcelFileDescriptor parcelFileDescriptor =
+ ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+
+ mBackupManagerService.adbRestore(parcelFileDescriptor);
+
+ verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- null,
- new Intent(),
- "dataManagementLabel"));
+ mBackupManagerService.acknowledgeAdbBackupOrRestore(
+ /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+ verify(mUserBackupManagerService)
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given either a
- * {@code null} data management label or {@code null} data management intent, but not both.
- */
+ // ---------------------------------------------
+ // Service tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void
- testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testDump_callsDumpForUser() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- null,
- "dataManagementLabel"));
+ mBackupManagerService.dump(fileDescriptor, printWriter, args);
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- null));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} succeeds if the caller has backup permission.
- */
- @Test
- public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mTransportManager)
- .updateTransportAttributes(
- eq(mTransportComponent),
- eq(mTransportName),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(dataManagementIntent),
- eq("dataManagementLabel"));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the caller
- * does not have backup permission.
- */
- @Test
- public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
- }
-
- /* Tests for request backup */
-
- @Mock private IBackupObserver mObserver;
-
- private void setUpForRequestBackup(String... packages) throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- for (String packageName : packages) {
- mShadowPackageManager.addPackage(packageName);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
- }
- setUpCurrentTransport(mTransportManager, mTransport);
- }
-
- private void tearDownForRequestBackup() {
- ShadowKeyValueBackupTask.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} throws a {@link SecurityException} if the caller does not have backup permission.
- */
- @Test
- public void testRequestBackup_whenPermissionDenied() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
- */
- @Test
- public void testRequestBackup_whenPackagesNull() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- IllegalArgumentException.class,
- () -> backupManagerService.requestBackup(null, mObserver, 0));
- verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
- * packages.
- */
- @Test
- public void testRequestBackup_whenPackagesEmpty() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- IllegalArgumentException.class,
- () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
- verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
- */
- @Test
- public void testRequestBackup_whenBackupDisabled() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(false);
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
- * through SUW.
- */
- @Test
- public void testRequestBackup_whenNotProvisioned() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setProvisioned(false);
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
- * registered.
- */
- @Test
- public void testRequestBackup_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport.unregistered());
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(true);
- backupManagerService.setProvisioned(true);
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
- verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
- * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
- */
- @Test
- public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- mShadowPackageManager.addPackage(PACKAGE_1);
- setUpCurrentTransport(mTransportManager, mTransport);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(true);
- backupManagerService.setProvisioned(true);
- // Haven't set PACKAGE_1 as eligible
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.SUCCESS);
- verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
- tearDownForRequestBackup();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
- * package succeeds.
- */
- @Test
- @Config(shadows = ShadowKeyValueBackupTask.class)
- public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
- setUpForRequestBackup(PACKAGE_1);
- BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(result).isEqualTo(BackupManager.SUCCESS);
- ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
- assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
- assertThat(shadowTask.getPendingFullBackups()).isEmpty();
- // TODO: Assert more about KeyValueBackupTask
- tearDownForRequestBackup();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
- * backup package succeeds.
- */
- @Test
- @Config(shadows = ShadowKeyValueBackupTask.class)
- public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
- setUpForRequestBackup(PACKAGE_1);
- ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
- BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(result).isEqualTo(BackupManager.SUCCESS);
- ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
- assertThat(shadowTask.getQueue()).isEmpty();
- assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
- // TODO: Assert more about KeyValueBackupTask
- tearDownForRequestBackup();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow()} clears the calling identity
- * for scheduling a job and then restores the original calling identity after the operation.
- */
- @Test
- @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
- public void testBackupNow_clearsCallingIdentityForJobScheduler() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- setUpPowerManager(backupManagerService);
- ShadowBinder.setCallingUid(1);
-
- backupManagerService.backupNow();
-
- assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
- assertThat(Binder.getCallingUid()).isEqualTo(1);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow()} restores the original calling
- * identity if an exception is thrown during execution.
- */
- @Test
- @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
- public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- setUpPowerManager(backupManagerService);
- ShadowBinder.setCallingUid(1);
-
- expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
- assertThat(ShadowKeyValueBackupJobException.getCallingUid())
- .isEqualTo(ShadowBinder.LOCAL_UID);
- assertThat(Binder.getCallingUid()).isEqualTo(1);
- }
-
- private BackupManagerService createBackupManagerServiceForRequestBackup() {
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(true);
- backupManagerService.setProvisioned(true);
- return backupManagerService;
- }
-
- /**
- * Test verifying that {@link BackupManagerService#BackupManagerService(Context, Trampoline,
- * HandlerThread, File, File, TransportManager)} posts a transport registration task to the
- * backup handler thread.
- */
- @Test
- public void testConstructor_postRegisterTransports() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
- createBackupManagerService();
-
- mShadowBackupLooper.runToEndOfTasks();
- verify(mTransportManager).registerTransports();
- }
-
- /**
- * Test verifying that the {@link BackupManagerService#BackupManagerService(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 BackupManagerService createBackupManagerService() {
- return new BackupManagerService(
- mContext,
- new Trampoline(mContext),
- mBackupThread,
- mBaseStateDir,
- mDataDir,
- mTransportManager);
- }
-
- private BackupManagerService createInitializedBackupManagerService() {
- return BackupManagerServiceTestUtils.createInitializedBackupManagerService(
- mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
- }
-
- private void setUpPowerManager(BackupManagerService backupManagerService) {
- PowerManager powerManagerMock = mock(PowerManager.class);
- when(powerManagerMock.getPowerSaveState(anyInt()))
- .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
- backupManagerService.setPowerManager(powerManagerMock);
- }
-
- /**
- * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
- * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
- */
- @Implements(KeyValueBackupJob.class)
- public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
- /**
- * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
- * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
- */
- public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
- ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
- throw new IllegalArgumentException();
- }
+ verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
}
}
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
new file mode 100644
index 0000000..9d43819
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowBinder;
+import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
+import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.shadows.ShadowSettings;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Tests for the per-user instance of the backup/restore system service {@link
+ * UserBackupManagerService} that performs operations for its target user.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAppBackupUtils.class})
+@Presubmit
+public class UserBackupManagerServiceTest {
+ private static final String TAG = "BMSTest";
+ private static final String PACKAGE_1 = "some.package.1";
+ private static final String PACKAGE_2 = "some.package.2";
+
+ @Mock private TransportManager mTransportManager;
+ private HandlerThread mBackupThread;
+ private ShadowLooper mShadowBackupLooper;
+ private File mBaseStateDir;
+ private File mDataDir;
+ private ShadowContextWrapper mShadowContext;
+ private Context mContext;
+ private TransportData mTransport;
+ private String mTransportName;
+ private ShadowPackageManager mShadowPackageManager;
+
+ /**
+ * Initialize state that {@link UserBackupManagerService} operations interact with. This
+ * includes setting up the transport, starting the backup thread, and creating backup data
+ * directories.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTransport = backupTransport();
+ mTransportName = mTransport.transportName;
+
+ // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
+ // we should not fail tests because of this. This is not flakiness, the exceptions thrown
+ // don't interfere with the tests.
+ mBackupThread = startSilentBackupThread(TAG);
+ mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
+
+ ContextWrapper context = RuntimeEnvironment.application;
+ mShadowPackageManager = shadowOf(context.getPackageManager());
+ mContext = context;
+ mShadowContext = shadowOf(context);
+
+ File cacheDir = mContext.getCacheDir();
+ // Corresponds to /data/backup
+ mBaseStateDir = new File(cacheDir, "base_state");
+ // Corresponds to /cache/backup_stage
+ mDataDir = new File(cacheDir, "data");
+ }
+
+ /**
+ * Clean up and reset state that was created for testing {@link UserBackupManagerService}
+ * operations.
+ */
+ @After
+ public void tearDown() throws Exception {
+ mBackupThread.quit();
+ ShadowAppBackupUtils.reset();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns the
+ * current destination string of inputted transport if the transport is registered.
+ */
+ @Test
+ public void testDestinationString() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenReturn("destinationString");
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String destination = backupManagerService.getDestinationString(mTransportName);
+
+ assertThat(destination).isEqualTo("destinationString");
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns
+ * {@code null} if the inputted transport is not registered.
+ */
+ @Test
+ public void testDestinationString_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenThrow(TransportNotRegisteredException.class);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String destination = backupManagerService.getDestinationString(mTransportName);
+
+ assertThat(destination).isNull();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testDestinationString_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenThrow(TransportNotRegisteredException.class);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.getDestinationString(mTransportName));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+ * {@code false} when the given app is not eligible for backup.
+ */
+ @Test
+ public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+ assertThat(result).isFalse();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+ * {@code true} when the given app is eligible for backup.
+ */
+ @Test
+ public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+ assertThat(result).isTrue();
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+ * returns an {@code array} of only apps that are eligible for backup from an {@array} of
+ * inputted apps.
+ */
+ @Test
+ public void testFilterAppsEligibleForBackup() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String[] filtered =
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {PACKAGE_1, PACKAGE_2});
+
+ assertThat(filtered).asList().containsExactly(PACKAGE_1);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+ * returns an empty {@code array} if no inputted apps are eligible for backup.
+ */
+ @Test
+ public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String[] filtered =
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {PACKAGE_1, PACKAGE_2});
+
+ assertThat(filtered).isEmpty();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+ * throws a {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {PACKAGE_1, PACKAGE_2}));
+ }
+
+ /* Tests for select transport */
+
+ private ComponentName mNewTransportComponent;
+ private TransportData mNewTransport;
+ private TransportMock mNewTransportMock;
+ private TransportData mOldTransport;
+ private TransportMock mOldTransportMock;
+
+ private void setUpForSelectTransport() throws Exception {
+ mNewTransport = backupTransport();
+ mNewTransportComponent = mNewTransport.getTransportComponent();
+ mOldTransport = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+ mNewTransportMock = transportMocks.get(0);
+ mOldTransportMock = transportMocks.get(1);
+ when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
+ .thenReturn(mOldTransport.transportName);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)}
+ * successfully switches the current transport to the inputted transport, returns the name of
+ * the old transport, and disposes of the transport client after the operation.
+ */
+ @Test
+ public void testSelectBackupTransport() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String oldTransport =
+ backupManagerService.selectBackupTransport(mNewTransport.transportName);
+
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testSelectBackupTransport_withoutPermission() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
+ * transport and disposes of the transport client after the operation.
+ */
+ @Test
+ public void testSelectBackupTransportAsync() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+ * transport and notifies the inputted callback of failure when it fails to register the
+ * transport.
+ */
+ @Test
+ public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+ verify(callback).onFailure(anyInt());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+ * transport and notifies the inputted callback of failure when the transport gets unregistered.
+ */
+ @Test
+ public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
+ setUpTransports(mTransportManager, mTransport.unregistered());
+ ComponentName newTransportComponent = mTransport.getTransportComponent();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
+ verify(callback).onFailure(anyInt());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
+ * have backup permission.
+ */
+ @Test
+ public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ComponentName newTransportComponent = mNewTransport.getTransportComponent();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.selectBackupTransportAsync(
+ newTransportComponent, mock(ISelectBackupTransportCallback.class)));
+ }
+
+ private String getSettingsTransport() {
+ return ShadowSettings.ShadowSecure.getString(
+ mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+ * the {@link ComponentName} of the currently selected transport.
+ */
+ @Test
+ public void testGetCurrentTransportComponent() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getCurrentTransportComponent())
+ .thenReturn(mTransport.getTransportComponent());
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+ assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+ * {@code null} if there is no currently selected transport.
+ */
+ @Test
+ public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+ assertThat(transportComponent).isNull();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+ * {@code null} if the currently selected transport is not registered.
+ */
+ @Test
+ public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getCurrentTransportComponent())
+ .thenThrow(TransportNotRegisteredException.class);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+ assertThat(transportComponent).isNull();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
+ }
+
+ /* Tests for updating transport attributes */
+
+ private static final int PACKAGE_UID = 10;
+ private ComponentName mTransportComponent;
+ private int mTransportUid;
+
+ private void setUpForUpdateTransportAttributes() throws Exception {
+ mTransportComponent = mTransport.getTransportComponent();
+ String transportPackage = mTransportComponent.getPackageName();
+
+ ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
+ shadowPackageManager.addPackage(transportPackage);
+ shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+
+ mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} succeeds if the uid of the transport
+ * is same as the uid of the caller.
+ */
+ @Test
+ public void
+ testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+ * the uid of the transport is not equal to the uid of the caller.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid + 1,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given a {@code null} transport component.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ null,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given a {@code null} transport name.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ null,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given a {@code null} destination string.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ null,
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given either a {@code null} data management label or {@code null} data management intent, but
+ * not both.
+ */
+ @Test
+ public void
+ testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ null,
+ "dataManagementLabel"));
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ null));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} succeeds if the caller has backup
+ * permission.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+ * the caller does not have backup permission.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /* Tests for request backup */
+
+ @Mock private IBackupObserver mObserver;
+
+ private void setUpForRequestBackup(String... packages) throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ for (String packageName : packages) {
+ mShadowPackageManager.addPackage(packageName);
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
+ }
+ setUpCurrentTransport(mTransportManager, mTransport);
+ }
+
+ private void tearDownForRequestBackup() {
+ ShadowKeyValueBackupTask.reset();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} throws a {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testRequestBackup_whenPermissionDenied() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
+ */
+ @Test
+ public void testRequestBackup_whenPackagesNull() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> backupManagerService.requestBackup(null, mObserver, 0));
+ verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
+ * packages.
+ */
+ @Test
+ public void testRequestBackup_whenPackagesEmpty() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
+ verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
+ */
+ @Test
+ public void testRequestBackup_whenBackupDisabled() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(false);
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
+ * through SUW.
+ */
+ @Test
+ public void testRequestBackup_whenNotProvisioned() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setProvisioned(false);
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
+ * registered.
+ */
+ @Test
+ public void testRequestBackup_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport.unregistered());
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(true);
+ backupManagerService.setProvisioned(true);
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
+ verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
+ * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
+ */
+ @Test
+ public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ mShadowPackageManager.addPackage(PACKAGE_1);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(true);
+ backupManagerService.setProvisioned(true);
+ // Haven't set PACKAGE_1 as eligible
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.SUCCESS);
+ verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
+ tearDownForRequestBackup();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
+ * package succeeds.
+ */
+ @Test
+ @Config(shadows = ShadowKeyValueBackupTask.class)
+ public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
+ setUpForRequestBackup(PACKAGE_1);
+ UserBackupManagerService backupManagerService =
+ createBackupManagerServiceForRequestBackup();
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(BackupManager.SUCCESS);
+ ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+ assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
+ assertThat(shadowTask.getPendingFullBackups()).isEmpty();
+ // TODO: Assert more about KeyValueBackupTask
+ tearDownForRequestBackup();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
+ * backup package succeeds.
+ */
+ @Test
+ @Config(shadows = ShadowKeyValueBackupTask.class)
+ public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
+ setUpForRequestBackup(PACKAGE_1);
+ ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
+ UserBackupManagerService backupManagerService =
+ createBackupManagerServiceForRequestBackup();
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(BackupManager.SUCCESS);
+ ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+ assertThat(shadowTask.getQueue()).isEmpty();
+ assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
+ // TODO: Assert more about KeyValueBackupTask
+ tearDownForRequestBackup();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#backupNow()} clears the calling identity
+ * for scheduling a job and then restores the original calling identity after the operation.
+ */
+ @Test
+ @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
+ public void testBackupNow_clearsCallingIdentityForJobScheduler() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ setUpPowerManager(backupManagerService);
+ ShadowBinder.setCallingUid(1);
+
+ backupManagerService.backupNow();
+
+ assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
+ assertThat(Binder.getCallingUid()).isEqualTo(1);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#backupNow()} restores the original
+ * calling identity if an exception is thrown during execution.
+ */
+ @Test
+ @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
+ public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ setUpPowerManager(backupManagerService);
+ ShadowBinder.setCallingUid(1);
+
+ expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
+ assertThat(ShadowKeyValueBackupJobException.getCallingUid())
+ .isEqualTo(ShadowBinder.LOCAL_UID);
+ assertThat(Binder.getCallingUid()).isEqualTo(1);
+ }
+
+ private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(true);
+ backupManagerService.setProvisioned(true);
+ return backupManagerService;
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#UserBackupManagerService(Context,
+ * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
+ * to the backup handler thread.
+ */
+ @Test
+ public void testConstructor_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(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+ }
+
+ private UserBackupManagerService createInitializedBackupManagerService() {
+ return BackupManagerServiceTestUtils.createInitializedUserBackupManagerService(
+ mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+ }
+
+ private void setUpPowerManager(UserBackupManagerService backupManagerService) {
+ PowerManager powerManagerMock = mock(PowerManager.class);
+ when(powerManagerMock.getPowerSaveState(anyInt()))
+ .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+ backupManagerService.setPowerManager(powerManagerMock);
+ }
+
+ /**
+ * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
+ * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
+ */
+ @Implements(KeyValueBackupJob.class)
+ public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
+ /**
+ * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
+ * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
+ */
+ public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+ ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+ throw new IllegalArgumentException();
+ }
+ }
+}
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/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index fd7ced2..423512c 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -1,10 +1,10 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index 6ee6eb6..a14cc51 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -46,6 +46,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -71,7 +72,7 @@
@Config(shadows = ShadowSlog.class)
@Presubmit
public class PerformInitializeTaskTest {
- @Mock private BackupManagerService mBackupManagerService;
+ @Mock private UserBackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private OnTaskFinishedListener mListener;
@Mock private IBackupTransport mTransportBinder;
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index a0afb5e..a1b8a95 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -26,7 +26,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.Log;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowSlog;
@@ -41,7 +41,7 @@
@Config(shadows = {ShadowEventLog.class, ShadowSlog.class})
@Presubmit
public class KeyValueBackupReporterTest {
- @Mock private BackupManagerService mBackupManagerService;
+ @Mock private UserBackupManagerService mBackupManagerService;
@Mock private IBackupObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
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 a69f007..1aa4999 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -26,7 +26,7 @@
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils
- .createInitializedBackupManagerService;
+ .createInitializedUserBackupManagerService;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils
.setUpBackupManagerServiceBasics;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils
@@ -103,12 +103,12 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.remote.RemoteCall;
@@ -178,7 +178,7 @@
@Mock private IBackupObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
@Mock private OnTaskFinishedListener mListener;
- private BackupManagerService mBackupManagerService;
+ private UserBackupManagerService mBackupManagerService;
private TransportData mTransport;
private ShadowLooper mShadowBackupLooper;
private Handler mBackupHandler;
@@ -227,7 +227,7 @@
setUpBinderCallerAndApplicationAsSystem(mApplication);
mBackupManagerService =
spy(
- createInitializedBackupManagerService(
+ createInitializedUserBackupManagerService(
mContext, mBaseStateDir, mDataDir, mTransportManager));
setUpBackupManagerServiceBasics(
mBackupManagerService,
@@ -720,7 +720,7 @@
}
/**
- * Agent unavailable means {@link BackupManagerService#bindToAgentSynchronous(ApplicationInfo,
+ * Agent unavailable means {@link UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo,
* int)} returns {@code null}.
*
* @see #setUpAgent(PackageData)
@@ -2597,7 +2597,7 @@
*
* <ul>
* <li>The transport being initialized with {@link IBackupTransport#initializeDevice()}
- * <li>{@link BackupManagerService#resetBackupState(File)} being called, which will:
+ * <li>{@link UserBackupManagerService#resetBackupState(File)} being called, which will:
* <ul>
* <li>Reset processed packages journal.
* <li>Reset current token to 0.
@@ -2617,7 +2617,7 @@
/**
* Forces transport initialization and call to {@link
- * BackupManagerService#resetBackupState(File)}
+ * UserBackupManagerService#resetBackupState(File)}
*/
private void deletePmStateFile() throws IOException {
Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 0e2b95b..859392d 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,8 +51,8 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
@@ -85,7 +85,7 @@
private static final long TOKEN_1 = 1L;
private static final long TOKEN_2 = 2L;
- @Mock private BackupManagerService mBackupManagerService;
+ @Mock private UserBackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private IRestoreObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
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 f307730..bacc44e 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,9 +37,9 @@
import android.util.Log;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.Trampoline;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import org.mockito.stubbing.Answer;
import org.robolectric.shadows.ShadowApplication;
@@ -49,26 +49,26 @@
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.atomic.AtomicReference;
-/** Test utils for {@link BackupManagerService} and friends. */
+/** 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.
*/
- public static BackupManagerService createInitializedBackupManagerService(
+ public static UserBackupManagerService createInitializedUserBackupManagerService(
Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
- return createInitializedBackupManagerService(
+ return createInitializedUserBackupManagerService(
context, startBackupThread(null), baseStateDir, dataDir, transportManager);
}
- public static BackupManagerService createInitializedBackupManagerService(
+ public static UserBackupManagerService createInitializedUserBackupManagerService(
Context context,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
- BackupManagerService backupManagerService =
- new BackupManagerService(
+ UserBackupManagerService backupManagerService =
+ new UserBackupManagerService(
context,
new Trampoline(context),
backupThread,
@@ -80,15 +80,16 @@
}
/**
- * Sets up basic mocks for {@link BackupManagerService} mock. If {@code backupManagerService} is
- * a spy, make sure you provide in the arguments the same objects that the original object uses.
+ * Sets up basic mocks for {@link UserBackupManagerService} mock. If {@code
+ * backupManagerService} is a spy, make sure you provide in the arguments the same objects that
+ * the original object uses.
*
* <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)}.
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
public static void setUpBackupManagerServiceBasics(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
Application application,
TransportManager transportManager,
PackageManager packageManager,
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/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index ca80664..ac5d2da 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -18,8 +18,8 @@
import android.annotation.Nullable;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.keyvalue.KeyValueBackupReporter;
import com.android.server.backup.keyvalue.KeyValueBackupTask;
@@ -54,7 +54,7 @@
@Implementation
protected void __constructor__(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
String transportDirName,
List<String> queue,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 228d4eb..2cebbeb 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -21,7 +21,7 @@
import android.app.backup.IRestoreObserver;
import android.content.pm.PackageInfo;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
@@ -47,7 +47,7 @@
sLastShadow = null;
}
- private BackupManagerService mBackupManagerService;
+ private UserBackupManagerService mBackupManagerService;
@Nullable private PackageInfo mPackage;
private boolean mIsFullSystemRestore;
@Nullable private String[] mFilterSet;
@@ -55,7 +55,7 @@
@Implementation
protected void __constructor__(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
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/Android.bp b/services/tests/mockingservicestests/Android.bp
new file mode 100644
index 0000000..ebc816d
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "FrameworksMockingServicesTests",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "services.core",
+ "services.net",
+ "androidx.test.runner",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
deleted file mode 100644
index b21b3e4..0000000
--- a/services/tests/mockingservicestests/Android.mk
+++ /dev/null
@@ -1,42 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- services.core \
- services.net \
- androidx.test.runner \
- mockito-target-extended-minus-junit4 \
- platform-test-annotations \
-
-LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
-
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
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..b2ec835
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 long IN_QUOTA_BUFFER_MILLIS = 30 * SECOND_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 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 + IN_QUOTA_BUFFER_MILLIS;
+ 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 + IN_QUOTA_BUFFER_MILLIS;
+ 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 + IN_QUOTA_BUFFER_MILLIS;
+ 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) + IN_QUOTA_BUFFER_MILLIS;
+ 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) + IN_QUOTA_BUFFER_MILLIS;
+ 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) + IN_QUOTA_BUFFER_MILLIS;
+ 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 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/servicestests/Android.bp b/services/tests/servicestests/Android.bp
new file mode 100644
index 0000000..e804342
--- /dev/null
+++ b/services/tests/servicestests/Android.bp
@@ -0,0 +1,104 @@
+//########################################################################
+// Build FrameworksServicesTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksServicesTests",
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+
+ "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl",
+ "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl",
+
+ "test-apps/JobTestApp/src/**/*.java",
+
+ "test-apps/SuspendTestApp/src/**/*.java",
+ ],
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.accessibility",
+ "services.appwidget",
+ "services.autofill",
+ "services.backup",
+ "services.core",
+ "services.devicepolicy",
+ "services.net",
+ "services.usage",
+ "guava",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "ShortcutManagerTestUtils",
+ "truth-prebuilt",
+ "testables",
+ "testng",
+ "ub-uiautomator",
+ "platformprotosnano",
+ "hamcrest-library",
+ "servicestests-utils",
+ ],
+
+ aidl: {
+ local_include_dirs: ["aidl"],
+ },
+
+ libs: [
+ "android.hidl.manager-V1.0-java",
+ "android.hardware.tv.cec-V1.0-java",
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdaidl",
+ "libui",
+ "libunwind",
+ "libutils",
+ ],
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+}
+
+java_library {
+ name: "servicestests-utils",
+ srcs: [
+ "utils/**/*.java",
+ ],
+ static_libs: [
+ "android-support-test",
+ "mockito-target-minus-junit4",
+ ],
+ libs: [
+ "android.test.runner",
+ ],
+}
+
+filegroup {
+ name: "servicestests-SuspendTestApp-files",
+ srcs: [
+ "src/com/android/server/pm/SuspendPackagesTest.java",
+ ],
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
deleted file mode 100644
index e2f8995..0000000
--- a/services/tests/servicestests/Android.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-#########################################################################
-# Build FrameworksServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- services.accessibility \
- services.appwidget \
- services.autofill \
- services.backup \
- services.core \
- services.devicepolicy \
- services.net \
- services.usage \
- guava \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-minus-junit4 \
- platform-test-annotations \
- ShortcutManagerTestUtils \
- truth-prebuilt \
- testables \
- testng \
- ub-uiautomator\
- platformprotosnano \
- hamcrest-library
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
- aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
-
-LOCAL_JAVA_LIBRARIES := \
- android.hidl.manager-V1.0-java \
- android.hardware.tv.cec-V1.0-java \
- android.test.mock \
- android.test.base android.test.runner \
-
-LOCAL_PACKAGE_NAME := FrameworksServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcutils \
- liblog \
- liblzma \
- libnativehelper \
- libnetdaidl \
- libui \
- libunwind \
- libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index fa17b61..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"
@@ -143,9 +144,6 @@
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
- <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
- android:showWhenLocked="true"/>
-
<activity android:name="com.android.server.pm.ShortcutTestActivity"
android:enabled="true" android:exported="true" />
@@ -206,12 +204,6 @@
</intent-filter>
</activity-alias>
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
-
<receiver android:name="com.android.server.appwidget.DummyAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
new file mode 100644
index 0000000..d4e53dd
--- /dev/null
+++ b/services/tests/servicestests/aidl/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "servicestests-aidl",
+ sdk_version: "current",
+ srcs: [
+ "com/android/servicestests/aidl/INetworkStateObserver.aidl",
+ "com/android/servicestests/aidl/ICmdReceiverService.aidl",
+ ],
+}
diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk
deleted file mode 100644
index 166da1d..0000000
--- a/services/tests/servicestests/aidl/Android.mk
+++ /dev/null
@@ -1,24 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
- com/android/servicestests/aidl/INetworkStateObserver.aidl \
- com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_MODULE := servicestests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations.xml b/services/tests/servicestests/res/raw/input_port_associations.xml
new file mode 100644
index 0000000..b10d541
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations.xml
@@ -0,0 +1,4 @@
+<ports>
+ <port display="0" input="USB1" />
+ <port display="1" input="USB2" />
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml
new file mode 100644
index 0000000..8eeb1f5
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml
@@ -0,0 +1,3 @@
+<ports>
+ <port display="a" input="USB1" />
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml
new file mode 100644
index 0000000..cf6e124
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml
@@ -0,0 +1,3 @@
+<ports>
+ <port Garbage data inside xml>
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/shortcut_share_targets.xml b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
new file mode 100644
index 0000000..ec696e9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
@@ -0,0 +1,87 @@
+<?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.
+ -->
+
+<!-- Test XML resource to read share-targets from, used in ShortcutManagerTest1.java -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ <shortcut
+ android:shortcutId="dummy_shortcut1"
+ android:enabled="true"
+ android:shortcutShortLabel="@string/shortcut_title1">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.test.somepackage"
+ android:targetClass="com.test.somepackage.someclass" />
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+
+ <!-- Valid share target definition -->
+ <share-target android:targetClass="com.test.directshare.TestActivity1">
+ <data
+ android:scheme="http"
+ android:host="www.google.com"
+ android:port="1234"
+ android:path="somePath"
+ android:pathPrefix="somePathPrefix"
+ android:pathPattern="somePathPattern"
+ android:mimeType="text/plain"/>
+ <category android:name="com.test.category.CATEGORY1"/>
+ <category android:name="com.test.category.CATEGORY2"/>
+ </share-target>
+
+ <!-- Share target missing data tag, will be dropped -->
+ <share-target android:targetClass="com.test.directshare.TestActivity">
+ <category android:name="com.test.category.CATEGORY2"/>
+ </share-target>
+
+ <!-- Share target missing target class, will be dropped -->
+ <share-target>
+ <data
+ android:scheme="file"
+ android:host="www.somehost.com"
+ android:port="1234"
+ android:mimeType="video/*"/>
+ <category android:name="com.test.category.CATEGORY3"/>
+ </share-target>
+
+ <shortcut
+ android:shortcutId="dummy_shortcut2"
+ android:enabled="true"
+ android:shortcutShortLabel="@string/shortcut_title1">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.test.somepackage"
+ android:targetClass="com.test.somepackage.someclass" />
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+
+ <!-- Share target missing category, will be dropped -->
+ <share-target android:targetClass="com.test.directshare.TestActivity">
+ <data
+ android:scheme="content"
+ android:mimeType="text/plain"/>
+ </share-target>
+
+ <!-- Valid share target definition -->
+ <share-target android:targetClass="com.test.directshare.TestActivity5">
+ <category android:name="com.test.category.CATEGORY5"/>
+ <category android:name="com.test.category.CATEGORY6"/>
+ <data android:mimeType="video/mp4"/>
+ <data
+ android:scheme="content"
+ android:mimeType="video/*"/>
+ </share-target>
+</shortcuts>
\ No newline at end of file
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..f70efdf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.Binder;
+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.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.CallSession;
+import com.android.server.BinderCallsStatsService.WorkSourceProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class BinderCallsStatsServiceTest {
+ @Test
+ public void weTrustOurselves() {
+ WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+ protected int getCallingUid() {
+ return Process.myUid();
+ }
+
+ protected int getCallingWorkSourceUid() {
+ return 1;
+ }
+ };
+ workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+ assertEquals(1, workSourceProvider.resolveWorkSourceUid());
+ }
+
+ @Test
+ public void workSourceSetIfCallerHasPermission() {
+ WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+ 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() {
+ WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+ 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());
+ }
+
+ @Test
+ public void workSourceSet() {
+ TestObserver observer = new TestObserver();
+ observer.callStarted(new Binder(), 0);
+ assertEquals(true, observer.workSourceSet);
+ }
+
+ static class TestObserver extends BinderCallsStatsService.BinderCallsObserver {
+ public boolean workSourceSet = false;
+
+ TestObserver() {
+ super(new NoopObserver(), new WorkSourceProvider());
+ }
+
+ @Override
+ protected void setThreadLocalWorkSourceUid(int uid) {
+ workSourceSet = true;
+ }
+ }
+
+
+ static class NoopObserver implements BinderInternal.Observer {
+ @Override
+ public CallSession callStarted(Binder binder, int code) {
+ return null;
+ }
+
+ @Override
+ public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+ }
+
+ @Override
+ public void callThrewException(CallSession s, Exception exception) {
+ }
+ }
+}
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/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index c7409d7..89c7b71 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -20,7 +20,6 @@
import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
-import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
@@ -197,23 +196,6 @@
}
@Test
- public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() {
- assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234"));
- }
-
- @Test
- public void testParseMemoryMaxUsageFromMemCg_emptyContents() {
- assertEquals(0, parseMemoryMaxUsageFromMemCg(""));
-
- assertEquals(0, parseMemoryMaxUsageFromMemCg(null));
- }
-
- @Test
- public void testParseMemoryMaxUsageFromMemCg_incorrectValue() {
- assertEquals(0, parseMemoryMaxUsageFromMemCg("memory"));
- }
-
- @Test
public void testParseMemoryStatFromProcfs_parsesCorrectValues() {
MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
assertEquals(1, stat.pgfault);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index d52051eec..479a19b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -34,7 +34,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.testutils.PackageManagerStub;
import org.junit.Before;
@@ -97,7 +97,7 @@
applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
applicationInfo.uid = Process.SYSTEM_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
- applicationInfo.packageName = BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+ applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
mPackageManagerStub);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 4774985..d43b677 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -51,8 +51,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.FileMetadata;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.restore.PerformAdbRestoreTask;
import com.android.server.backup.restore.RestorePolicy;
import com.android.server.backup.testutils.PackageManagerStub;
@@ -150,7 +150,7 @@
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(fileMetadata.path).isEqualTo(BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ assertThat(fileMetadata.path).isEqualTo(UserBackupManagerService.BACKUP_MANIFEST_FILENAME);
tarBackupReader.skipTarPadding(fileMetadata.size);
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..c3a0dda 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4163,7 +4163,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));
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/input/ConfigurationProcessorTest.java b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java
new file mode 100644
index 0000000..636aa37
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.input;
+
+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 android.content.Context;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * atest ConfigurationProcessorTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ConfigurationProcessorTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
+ @Test
+ public void testGetInputPortAssociations() {
+ final int res = com.android.frameworks.servicestests.R.raw.input_port_associations;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ List<Pair<String, String>> associations = null;
+ try {
+ associations = ConfigurationProcessor.processInputPortAssociations(xml);
+ } catch (Exception e) {
+ fail("Could not process xml file for input associations");
+ }
+ assertNotNull(associations);
+ assertEquals(2, associations.size());
+ assertTrue(associations.contains(Pair.create("USB1", "0")));
+ assertTrue(associations.contains(Pair.create("USB2", "1")));
+ }
+
+ @Test
+ public void testGetInputPortAssociationsBadDisplayport() {
+ final int res =
+ com.android.frameworks.servicestests.R.raw.input_port_associations_bad_displayport;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ List<Pair<String, String>> associations = null;
+ try {
+ associations = ConfigurationProcessor.processInputPortAssociations(xml);
+ } catch (Exception e) {
+ fail("Could not process xml file for input associations");
+ }
+ assertNotNull(associations);
+ assertEquals(0, associations.size());
+ }
+
+ @Test
+ public void testGetInputPortAssociationsEmptyConfig() {
+ final int res = com.android.frameworks.servicestests.R.raw.input_port_associations_bad_xml;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ try {
+ ConfigurationProcessor.processInputPortAssociations(xml);
+ fail("Parsing should fail, because xml contains bad data");
+ } catch (Exception e) {
+ // This is expected
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
new file mode 100644
index 0000000..fc2dcb9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.test.AndroidTestCase;
+
+import com.android.internal.util.HexDump;
+
+public class SP800DeriveTests extends AndroidTestCase {
+ public void testFixedInput() throws Exception {
+ // CAVP: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/key-derivation
+ byte[] keyBytes = HexDump.hexStringToByteArray(
+ "e204d6d466aad507ffaf6d6dab0a5b26"
+ + "152c9e21e764370464e360c8fbc765c6");
+ SP800Derive sk = new SP800Derive(keyBytes);
+ byte[] fixedInput = HexDump.hexStringToByteArray(
+ "7b03b98d9f94b899e591f3ef264b71b1"
+ + "93fba7043c7e953cde23bc5384bc1a62"
+ + "93580115fae3495fd845dadbd02bd645"
+ + "5cf48d0f62b33e62364a3a80");
+ byte[] res = sk.fixedInput(fixedInput);
+ assertEquals((
+ "770dfab6a6a4a4bee0257ff335213f78"
+ + "d8287b4fd537d5c1fffa956910e7c779").toUpperCase(), HexDump.toHexString(res));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 113ee2d..99b827c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.net;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -72,7 +72,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -142,12 +141,14 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
-import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkStatsManagerInternal;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
import com.google.common.util.concurrent.AbstractFuture;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -162,9 +163,6 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -195,15 +193,6 @@
/**
* Tests for {@link NetworkPolicyManagerService}.
- *
- * <p>Typical usage:
- *
- * <pre><code>
- m -j32 FrameworksServicesTests && adb install -r -g \
- ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
- adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \
- "com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner"
- * </code></pre>
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -376,7 +365,7 @@
return null;
}
}).when(mActivityManager).registerUidObserver(any(), anyInt(),
- eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class));
+ eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
@@ -425,7 +414,7 @@
// catch INetworkManagementEventObserver during systemReady()
final ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
- ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+ ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
@@ -843,6 +832,18 @@
assertTrue(mService.isUidForeground(UID_B));
}
+ @Test
+ public void testAppIdleTempWhitelisting() throws Exception {
+ mService.setAppIdleWhitelist(UID_A, true);
+ mService.setAppIdleWhitelist(UID_B, false);
+ int[] whitelistedIds = mService.getAppIdleWhitelist();
+ assertTrue(Arrays.binarySearch(whitelistedIds, UID_A) >= 0);
+ assertTrue(Arrays.binarySearch(whitelistedIds, UID_B) < 0);
+ assertFalse(mService.isUidIdle(UID_A));
+ // Can't currently guarantee UID_B's app idle state.
+ // TODO: expand with multiple app idle states.
+ }
+
private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
@@ -1770,7 +1771,7 @@
}
private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
- long limitBytes, boolean inferred){
+ long limitBytes, boolean inferred) {
final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
@@ -1868,7 +1869,7 @@
}
private static void assertNotificationType(int expected, String actualTag) {
- assertEquals("notification type mismatch for '" + actualTag +"'",
+ assertEquals("notification type mismatch for '" + actualTag + "'",
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
}
@@ -1902,7 +1903,8 @@
final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
final Intent intent = future.get(5, TimeUnit.SECONDS);
assertNotNull("Didn't get a " + action + "intent in 5 seconds");
- assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage());
+ assertEquals("Wrong package on " + action + " intent",
+ expectedPackage, intent.getPackage());
}
// TODO: replace by Truth, Hamcrest, or a similar tool.
@@ -1923,7 +1925,7 @@
}
if (errors.length() > 0) {
fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected)
- + ", actual=" + Arrays.toString(actual) +") failed: \n" + errors);
+ + ", actual=" + Arrays.toString(actual) + ") failed: \n" + errors);
}
}
@@ -1986,7 +1988,7 @@
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Log.d(TAG,"counting down on answer: " + invocation);
+ Log.d(TAG, "counting down on answer: " + invocation);
latch.countDown();
return null;
}
@@ -2024,8 +2026,8 @@
final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
final File netConfigFile = new File(mPolicyDir, "netpolicy.xml");
Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath);
- try (final InputStream in = context.getResources().getAssets().open(assetPath);
- final OutputStream out = new FileOutputStream(netConfigFile)) {
+ try (InputStream in = context.getResources().getAssets().open(assetPath);
+ OutputStream out = new FileOutputStream(netConfigFile)) {
Streams.copy(in, out);
}
}
@@ -2037,9 +2039,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NetPolicyXml {
-
- public String value() default "";
-
+ String value() default "";
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index a3348c2..1f5c64e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -91,8 +91,8 @@
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
-
import com.android.server.wm.ActivityTaskManagerInternal;
+
import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -105,8 +105,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -762,6 +760,7 @@
LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
@@ -963,6 +962,10 @@
return getInstrumentation().getContext();
}
+ protected Context getClientContext() {
+ return mClientContext;
+ }
+
protected ShortcutManager getManager() {
return mManager;
}
@@ -1792,6 +1795,15 @@
}
/**
+ * @return all share targets stored internally for the caller.
+ */
+ protected List<ShareTargetInfo> getCallerShareTargets() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ return p == null ? null : p.getAllShareTargetsForTest();
+ }
+
+ /**
* @return all shortcuts owned by caller that are actually visible via ShortcutManager.
* See also {@link #getCallerShortcuts}.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index d798865..ce59e6e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -31,6 +31,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.UsesPermissionInfo;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -464,6 +465,7 @@
pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
pkg.requestedPermissions.add("foo7");
+ pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7"));
pkg.implicitPermissions.add("foo25");
pkg.protectedBroadcasts = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index fa73447..3172afb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -106,6 +106,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
@@ -398,7 +399,7 @@
assertEquals(3, mManager.getRemainingCallCount());
}
- public void testPublishWithNoActivity() {
+ public void testPublishWithNoActivity() {
// If activity is not explicitly set, use the default one.
mRunningUsers.put(USER_10, true);
@@ -8015,4 +8016,56 @@
assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
}
+
+ public void testParseShareTargetsFromManifest() {
+ // These values must exactly match the content of shortcuts_share_targets.xml resource
+ List<ShareTargetInfo> expectedValues = new ArrayList<>();
+ expectedValues.add(new ShareTargetInfo(
+ new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
+ "http", "www.google.com", "1234", "somePath", "somePathPattern",
+ "somePathPrefix", "text/plain")}, "com.test.directshare.TestActivity1",
+ new String[]{"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"}));
+ expectedValues.add(new ShareTargetInfo(new ShareTargetInfo.TargetData[]{
+ new ShareTargetInfo.TargetData(null, null, null, null, null, null, "video/mp4"),
+ new ShareTargetInfo.TargetData("content", null, null, null, null, null, "video/*")},
+ "com.test.directshare.TestActivity5",
+ new String[]{"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}));
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_share_targets);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ List<ShareTargetInfo> shareTargets = getCallerShareTargets();
+
+ assertNotNull(shareTargets);
+ assertEquals(expectedValues.size(), shareTargets.size());
+
+ for (int i = 0; i < expectedValues.size(); i++) {
+ ShareTargetInfo expected = expectedValues.get(i);
+ ShareTargetInfo actual = shareTargets.get(i);
+
+ assertEquals(expected.mTargetData.length, actual.mTargetData.length);
+ for (int j = 0; j < expected.mTargetData.length; j++) {
+ assertEquals(expected.mTargetData[j].mScheme, actual.mTargetData[j].mScheme);
+ assertEquals(expected.mTargetData[j].mHost, actual.mTargetData[j].mHost);
+ assertEquals(expected.mTargetData[j].mPort, actual.mTargetData[j].mPort);
+ assertEquals(expected.mTargetData[j].mPath, actual.mTargetData[j].mPath);
+ assertEquals(expected.mTargetData[j].mPathPrefix,
+ actual.mTargetData[j].mPathPrefix);
+ assertEquals(expected.mTargetData[j].mPathPattern,
+ actual.mTargetData[j].mPathPattern);
+ assertEquals(expected.mTargetData[j].mMimeType, actual.mTargetData[j].mMimeType);
+ }
+
+ assertEquals(expected.mTargetClass, actual.mTargetClass);
+
+ assertEquals(expected.mCategories.length, actual.mCategories.length);
+ for (int j = 0; j < expected.mCategories.length; j++) {
+ assertEquals(expected.mCategories[j], actual.mCategories[j]);
+ }
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 76d52fd..9b59f91 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -144,8 +144,8 @@
assertExpectException(
IllegalArgumentException.class, "Short label must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
});
@@ -153,15 +153,15 @@
// same for add.
assertExpectException(
IllegalArgumentException.class, "Short label must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
});
assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.setShortLabel("x")
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -169,8 +169,8 @@
// same for add.
assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.setShortLabel("x")
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -178,7 +178,7 @@
assertExpectException(
IllegalStateException.class, "does not belong to package", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
.setActivity(new ComponentName("xxx", "s"))
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -187,7 +187,7 @@
// same for add.
assertExpectException(
IllegalStateException.class, "does not belong to package", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
.setActivity(new ComponentName("xxx", "s"))
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -198,24 +198,24 @@
assertExpectException(
IllegalStateException.class, "is not main", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext(), "s"))
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
});
// For add
assertExpectException(
IllegalStateException.class, "is not main", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext(), "s"))
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
});
// For update
assertExpectException(
IllegalStateException.class, "is not main", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext(), "s"))
.build();
assertTrue(getManager().updateShortcuts(list(si)));
});
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..c1963da 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
@@ -294,6 +294,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..a9d4519
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.emptySet;
+
+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.Set;
+
+
+/**
+ * Tests for {@link SignedConfig}
+ */
+@RunWith(AndroidJUnit4.class)
+public class SignedConfigTest {
+
+ private static Set<String> setOf(String... values) {
+ return Sets.newHashSet(values);
+ }
+
+ @Test
+ public void testParsePerSdkConfigSdkMinMax() throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject("{\"minSdk\":2, \"maxSdk\": 3, \"values\": []}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, emptySet());
+ assertThat(config.minSdk).isEqualTo(2);
+ assertThat(config.maxSdk).isEqualTo(3);
+ }
+
+ @Test
+ public void testParsePerSdkConfigNoMinSdk() throws JSONException {
+ JSONObject json = new JSONObject("{\"maxSdk\": 3, \"values\": []}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigNoMaxSdk() throws JSONException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"values\": []}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigNoValues() throws JSONException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigSdkNullMinSdk() throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject("{\"minSdk\":null, \"maxSdk\": 3, \"values\": []}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigSdkNullMaxSdk() throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject("{\"minSdk\":1, \"maxSdk\": null, \"values\": []}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigNullValues() throws JSONException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": null}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigZeroValues()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": []}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+ assertThat(config.values).hasSize(0);
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKey()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+ assertThat(config.values).containsExactly("a", "1");
+ }
+
+ @Test
+ public void testParsePerSdkConfigMultiKeys()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}, "
+ + "{\"key\":\"c\", \"value\": \"2\"}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(
+ json, setOf("a", "b", "c"));
+ assertThat(config.values).containsExactly("a", "1", "c", "2");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyNotAllowed() throws JSONException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, setOf("b"));
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyNoValue()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\"}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+ assertThat(config.values).containsExactly("a", null);
+ }
+
+ @Test
+ public void testParsePerSdkConfigValuesInvalid() throws JSONException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": \"foo\"}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigConfigEntryInvalid() throws JSONException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [1, 2]}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigConfigEntryNull() throws JSONException {
+ JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [null]}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, emptySet());
+ fail("Expected InvalidConfigException or JSONException");
+ } catch (JSONException | InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParseVersion() throws InvalidConfigException {
+ SignedConfig config = SignedConfig.parse(
+ "{\"version\": 1, \"config\": []}", emptySet());
+ assertThat(config.version).isEqualTo(1);
+ }
+
+ @Test
+ public void testParseVersionInvalid() {
+ try {
+ SignedConfig.parse("{\"version\": \"notanint\", \"config\": []}", emptySet());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseNoVersion() {
+ try {
+ SignedConfig.parse("{\"config\": []}", emptySet());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseNoConfig() {
+ try {
+ SignedConfig.parse("{\"version\": 1}", emptySet());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseConfigNull() {
+ try {
+ SignedConfig.parse("{\"version\": 1, \"config\": null}", emptySet());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseVersionNull() {
+ try {
+ SignedConfig.parse("{\"version\": null, \"config\": []}", emptySet());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseConfigInvalidEntry() {
+ try {
+ SignedConfig.parse("{\"version\": 1, \"config\": [{}]}", emptySet());
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testParseSdkConfigSingle() throws InvalidConfigException {
+ SignedConfig config = SignedConfig.parse(
+ "{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}]}",
+ emptySet());
+ assertThat(config.perSdkConfig).hasSize(1);
+ }
+
+ @Test
+ public void testParseSdkConfigMultiple() throws InvalidConfigException {
+ SignedConfig config = SignedConfig.parse(
+ "{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}, "
+ + "{\"minSdk\": 2, \"maxSdk\": 2, \"values\": []}]}", emptySet());
+ 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/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
deleted file mode 100644
index 415b5d9..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ /dev/null
@@ -1,253 +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 android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link AppWindowContainerController}.
- *
- * atest FrameworksServicesTests:AppWindowContainerControllerTests
- */
-@FlakyTest(bugId = 74078662)
-@SmallTest
-@Presubmit
-public class AppWindowContainerControllerTests extends WindowTestsBase {
-
- private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
-
- @Test
- public void testRemoveContainer() {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
-
- // Assert token was added to display.
- assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
- // Assert that the container was created and linked.
- assertNotNull(controller.mContainer);
-
- controller.removeContainer(mDisplayContent.getDisplayId());
-
- // Assert token was remove from display.
- assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
- // Assert that the container was removed.
- assertNull(controller.mContainer);
- }
-
- @Test
- public void testSetOrientation() {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
-
- // Assert orientation is unspecified to start.
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
- controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(),
- EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */);
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation());
-
- controller.removeContainer(mDisplayContent.getDisplayId());
- // Assert orientation is unspecified to after container is removed.
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
- // Reset display frozen state
- mWm.mDisplayFrozen = false;
- }
-
- private void assertHasStartingWindow(AppWindowToken atoken) {
- assertNotNull(atoken.startingSurface);
- assertNotNull(atoken.startingData);
- assertNotNull(atoken.startingWindow);
- }
-
- private void assertNoStartingWindow(AppWindowToken atoken) {
- assertNull(atoken.startingSurface);
- assertNull(atoken.startingWindow);
- assertNull(atoken.startingData);
- atoken.forAllWindows(windowState -> {
- assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
- }, true);
- }
-
- @Test
- public void testCreateRemoveStartingWindow() {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
- controller.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
- final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent);
- assertHasStartingWindow(atoken);
- controller.removeStartingWindow();
- waitUntilHandlersIdle();
- assertNoStartingWindow(atoken);
- }
-
- @Test
- public void testAddRemoveRace() {
- // There was once a race condition between adding and removing starting windows
- for (int i = 0; i < 1000; i++) {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
- controller.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- controller.removeStartingWindow();
- waitUntilHandlersIdle();
- assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
-
- controller.getAppWindowToken(
- mDisplayContent).getParent().getParent().removeImmediately();
- }
- }
-
- @Test
- public void testTransferStartingWindow() {
- final WindowTestUtils.TestAppWindowContainerController controller1 =
- createAppWindowController();
- final WindowTestUtils.TestAppWindowContainerController controller2 =
- createAppWindowController();
- controller1.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
- controller2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
- true, true, false, true, false, false);
- waitUntilHandlersIdle();
- assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
- assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
- }
-
- @Test
- public void testTransferStartingWindowWhileCreating() {
- final WindowTestUtils.TestAppWindowContainerController controller1 =
- createAppWindowController();
- final WindowTestUtils.TestAppWindowContainerController controller2 =
- createAppWindowController();
- ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
-
- // Surprise, ...! Transfer window in the middle of the creation flow.
- controller2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
- true, true, false, true, false, false);
- });
- controller1.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
- assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
- assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
- }
-
- @Test
- public void testTryTransferStartingWindowFromHiddenAboveToken() {
-
- // Add two tasks on top of each other.
- TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(this);
- final WindowTestUtils.TestAppWindowContainerController controllerTop =
- createAppWindowController(taskController);
- final WindowTestUtils.TestAppWindowContainerController controllerBottom =
- createAppWindowController(taskController);
-
- // Add a starting window.
- controllerTop.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
-
- // Make the top one invisible, and try transfering the starting window from the top to the
- // bottom one.
- controllerTop.setVisibility(false, false);
- controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-
- // Assert that the bottom window now has the starting window.
- assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent));
- assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent));
- }
-
- @Test
- public void testReparent() {
- final StackWindowController stackController =
- createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController1 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
- final WindowTestUtils.TestAppWindowContainerController appWindowController1 =
- createAppWindowController(taskController1);
- final WindowTestUtils.TestTaskWindowContainerController taskController2 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
- final WindowTestUtils.TestAppWindowContainerController appWindowController2 =
- createAppWindowController(taskController2);
- final WindowTestUtils.TestTaskWindowContainerController taskController3 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
-
- try {
- appWindowController1.reparent(taskController1, 0);
- fail("Should not be able to reparent to the same parent");
- } catch (IllegalArgumentException e) {
- // Expected
- }
-
- try {
- taskController3.setContainer(null);
- appWindowController1.reparent(taskController3, 0);
- fail("Should not be able to reparent to a task that doesn't have a container");
- } catch (IllegalArgumentException e) {
- // Expected
- }
-
- // Reparent the app window and ensure that it is moved
- appWindowController1.reparent(taskController2, 0);
- assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent());
- assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer)
- .positionInParent());
- assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer)
- .positionInParent());
- }
-
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController() {
- return createAppWindowController(
- new WindowTestUtils.TestTaskWindowContainerController(this));
- }
-
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
- WindowTestUtils.TestTaskWindowContainerController taskController) {
- return new WindowTestUtils.TestAppWindowContainerController(taskController);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
deleted file mode 100644
index a91c5e7..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ /dev/null
@@ -1,422 +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.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.wm.utils.WmDisplayCutout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
-
- private DisplayFrames mFrames;
- private WindowState mWindow;
- private int mRotation = ROTATION_0;
- private boolean mHasDisplayCutout;
-
- @Before
- public void setUp() throws Exception {
- updateDisplayFrames();
-
- mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
- // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
- // changing those frames.
- doNothing().when(mWindow).computeFrameLw();
-
- final WindowManager.LayoutParams attrs = mWindow.mAttrs;
- attrs.width = MATCH_PARENT;
- attrs.height = MATCH_PARENT;
- attrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- attrs.format = PixelFormat.TRANSLUCENT;
- }
-
- public void setRotation(int rotation) {
- mRotation = rotation;
- updateDisplayFrames();
- }
-
- public void addDisplayCutout() {
- mHasDisplayCutout = true;
- updateDisplayFrames();
- }
-
- private void updateDisplayFrames() {
- final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
- mHasDisplayCutout);
- mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
- }
-
- @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);
-
- assertEquals(0, mWindow.mAttrs.systemUiVisibility);
- assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout() {
- addDisplayCutout();
-
- 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);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_never() {
- addDisplayCutout();
-
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
-
- 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);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
- addDisplayCutout();
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- 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_withDisplayCutout_fullscreen() {
- addDisplayCutout();
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- addWindow(mWindow);
-
- 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);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
- addDisplayCutout();
-
- 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);
-
- 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);
-
- 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);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_seascape() {
- addDisplayCutout();
- setRotation(ROTATION_270);
- addWindow(mWindow);
-
- 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);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
-
- 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);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
- 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);
-
- 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);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- 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);
-
- 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 */);
-
- 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);
-
- 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 */);
-
- 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();
-
- 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()));
- }
-
- @Test
- public void layoutHint_appWindowInTask_outsideContentFrame() {
- // 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);
-
- 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);
-
- 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()));
- }
-
- /**
- * Asserts that {@code actual} is inset by the given amounts from the full display rect.
- *
- * Convenience wrapper for when only the top and bottom inset are non-zero.
- */
- private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
- int expectedInsetBottom) {
- assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
- }
-
- /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
- private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
- int expectedInsetRight, int expectedInsetBottom) {
- assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
- mFrames.mDisplayWidth - expectedInsetRight,
- mFrames.mDisplayHeight - expectedInsetBottom), actual);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
deleted file mode 100644
index 60c0459..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ /dev/null
@@ -1,363 +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 android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.view.IApplicationToken;
-import android.view.IWindow;
-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.
- */
-public class WindowTestUtils {
- private static int sNextTaskId = 0;
-
- /**
- * Creates a mock instance of {@link StackWindowController}.
- */
- 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;
- }
-
- /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
- public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
- int userId) {
- synchronized (service.mGlobalLock) {
- final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
- new ActivityManager.TaskDescription(), null);
- stack.addTask(newTask, POSITION_TOP);
- return newTask;
- }
- }
-
- /**
- * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
- * normally be mocked out.
- */
- public static class TestTaskStack extends TaskStack {
- TestTaskStack(WindowManagerService service, int stackId) {
- super(service, stackId, null);
- }
-
- @Override
- void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
- // Do nothing.
- }
- }
-
- static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
- synchronized (dc.mService.mGlobalLock) {
- return new TestAppWindowToken(dc);
- }
- }
-
- /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
- public static class TestAppWindowToken extends AppWindowToken {
- boolean mOnTop = false;
- private Transaction mPendingTransactionOverride;
-
- private TestAppWindowToken(DisplayContent dc) {
- super(dc.mService, new IApplicationToken.Stub() {
- public String getName() {return null;}
- }, new ComponentName("", ""), false, dc, true /* fillsParent */);
- }
-
- TestAppWindowToken(WindowManagerService service, IApplicationToken token,
- ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
- int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
- boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
- super(service, token, activityComponent, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
- orientation, rotationAnimationHint, configChanges, launchTaskBehind,
- alwaysFocusable, controller);
- }
-
- int getWindowsCount() {
- return mChildren.size();
- }
-
- boolean hasWindow(WindowState w) {
- return mChildren.contains(w);
- }
-
- WindowState getFirstChild() {
- return mChildren.peekFirst();
- }
-
- WindowState getLastChild() {
- return mChildren.peekLast();
- }
-
- int positionInParent() {
- return getParent().mChildren.indexOf(this);
- }
-
- void setIsOnTop(boolean onTop) {
- mOnTop = onTop;
- }
-
- @Override
- boolean isOnTop() {
- return mOnTop;
- }
-
- void setPendingTransaction(Transaction transaction) {
- mPendingTransactionOverride = transaction;
- }
-
- @Override
- public Transaction getPendingTransaction() {
- return mPendingTransactionOverride == null
- ? super.getPendingTransaction()
- : mPendingTransactionOverride;
- }
- }
-
- static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
- return createTestWindowToken(type, dc, false /* persistOnEmpty */);
- }
-
- static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
- boolean persistOnEmpty) {
- synchronized (dc.mService.mGlobalLock) {
- return new TestWindowToken(type, dc, persistOnEmpty);
- }
- }
-
- /* Used so we can gain access to some protected members of the {@link WindowToken} class */
- public static class TestWindowToken extends WindowToken {
-
- private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
- super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc,
- false /* ownerCanManageAppTokens */);
- }
-
- int getWindowsCount() {
- return mChildren.size();
- }
-
- boolean hasWindow(WindowState w) {
- return mChildren.contains(w);
- }
- }
-
- /* Used so we can gain access to some protected members of the {@link Task} class */
- public static class TestTask extends Task {
- boolean mShouldDeferRemoval = false;
- boolean mOnDisplayChangedCalled = false;
- private boolean mIsAnimating = false;
-
- TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
- int resizeMode, boolean supportsPictureInPicture,
- TaskWindowContainerController controller) {
- super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
- new ActivityManager.TaskDescription(), controller);
- }
-
- boolean shouldDeferRemoval() {
- return mShouldDeferRemoval;
- }
-
- int positionInParent() {
- return getParent().mChildren.indexOf(this);
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- super.onDisplayChanged(dc);
- mOnDisplayChangedCalled = true;
- }
-
- @Override
- boolean isSelfAnimating() {
- return mIsAnimating;
- }
-
- void setLocalIsAnimating(boolean isAnimating) {
- mIsAnimating = isAnimating;
- }
- }
-
- /**
- * 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 class TestAppWindowContainerController extends AppWindowContainerController {
-
- final IApplicationToken mToken;
-
- TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
- this(taskController, new TestIApplicationToken());
- }
-
- TestAppWindowContainerController(TestTaskWindowContainerController taskController,
- IApplicationToken token) {
- super(taskController, token, new ComponentName("", "") /* activityComponent */,
- null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED,
- true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */,
- false /* voiceInteraction */, false /* launchTaskBehind */,
- false /* alwaysFocusable */, 0 /* targetSdkVersion */,
- 0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */,
- taskController.mService);
- mToken = token;
- }
-
- @Override
- AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
- ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos,
- boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
- int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
- boolean alwaysFocusable, AppWindowContainerController controller) {
- return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
- orientation,
- rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
- controller);
- }
-
- AppWindowToken getAppWindowToken(DisplayContent dc) {
- return (AppWindowToken) dc.getWindowToken(mToken.asBinder());
- }
- }
-
- public static class TestIApplicationToken implements IApplicationToken {
-
- private final Binder mBinder = new Binder();
- @Override
- public IBinder asBinder() {
- return mBinder;
- }
- @Override
- public String getName() {
- return null;
- }
- }
-
- /** Used to track resize reports. */
- public static class TestWindowState extends WindowState {
- boolean resizeReported;
-
- TestWindowState(WindowManagerService service, Session session, IWindow window,
- WindowManager.LayoutParams attrs, WindowToken token) {
- super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
- false /* ownerCanAddInternalSystemWindow */);
- }
-
- @Override
- void reportResized() {
- super.reportResized();
- resizeReported = true;
- }
-
- @Override
- public boolean isGoneForLayoutLw() {
- return false;
- }
-
- @Override
- void updateResizingWindowIfNeeded() {
- // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
- // the system that it can actually update the window.
- boolean hadSurface = mHasSurface;
- mHasSurface = true;
-
- super.updateResizingWindowIfNeeded();
-
- mHasSurface = hadSurface;
- }
- }
-}
diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/services/tests/servicestests/test-apps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
new file mode 100644
index 0000000..13e6644
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "ConnTestApp",
+
+ test_suites: ["device-tests"],
+
+ static_libs: ["servicestests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
new file mode 100644
index 0000000..ae1eca7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "JobTestApp",
+
+ sdk_version: "current",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
deleted file mode 100644
index 7893c91..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.mk
+++ /dev/null
@@ -1,30 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := JobTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
new file mode 100644
index 0000000..7257275
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
@@ -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.
+
+android_test_helper_app {
+ name: "SuspendTestApp",
+
+ test_suites: ["device-tests"],
+
+ static_libs: [
+ "androidx.test.runner",
+ "ub-uiautomator",
+ ],
+
+ srcs: [
+ "**/*.java",
+ ":servicestests-SuspendTestApp-files",
+ ],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+
+ platform_apis: true,
+
+}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
deleted file mode 100644
index ab222b9..0000000
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ /dev/null
@@ -1,34 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
-
-LOCAL_PACKAGE_NAME := SuspendTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
new file mode 100644
index 0000000..c2cb6881
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+java_library {
+ name: "ShortcutManagerTestUtils",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "mockito-target",
+ "android.test.runner.stubs",
+ ],
+
+ sdk_version: "test_current",
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
deleted file mode 100644
index 019bcbd3..0000000
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
- mockito-target \
- android.test.runner.stubs
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ShortcutManagerTestUtils
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
new file mode 100644
index 0000000..ca8cc0d
--- /dev/null
+++ b/services/tests/uiservicestests/Android.bp
@@ -0,0 +1,58 @@
+//########################################################################
+// Build FrameworksUiServicesTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksUiServicesTests",
+
+ // Include test java files
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "services.accessibility",
+ "services.core",
+ "services.devicepolicy",
+ "services.net",
+ "services.usage",
+ "guava",
+ "android-support-test",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ dxflags: ["--multi-dex"],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ compile_multilib: "both",
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdaidl",
+ "libui",
+ "libunwindstack",
+ "libutils",
+ ],
+}
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
deleted file mode 100644
index f3f4355..0000000
--- a/services/tests/uiservicestests/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-#########################################################################
-# Build FrameworksUiServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include test java files and source from notifications package.
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(call all-java-files-under, ../../core/java/com/android/server/notification) \
- $(call all-java-files-under, ../../core/java/com/android/server/slice) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- services.accessibility \
- services.core \
- services.devicepolicy \
- services.net \
- services.usage \
- guava \
- android-support-test \
- mockito-target-inline-minus-junit4 \
- platform-test-annotations \
- testables
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MULTILIB := both
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libmultiplejvmtiagentsinterferenceagent \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcutils \
- liblog \
- liblzma \
- libnativehelper \
- libnetdaidl \
- libui \
- libunwindstack \
- libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_PACKAGE)
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 3266b8b..d950360 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,10 +38,8 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
-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_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -68,16 +66,15 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.Application;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
-import android.app.ITransientNotification;
-import android.app.IUriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
@@ -100,7 +97,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
@@ -116,7 +112,6 @@
import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.Log;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
@@ -288,6 +283,7 @@
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+ when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -1735,7 +1731,8 @@
}
@Test
- public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
+ throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
@@ -2509,6 +2506,7 @@
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+ verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
}
@Test
@@ -2517,8 +2515,11 @@
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
}
@@ -2529,8 +2530,12 @@
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(
+ eq(r.sbn), eq(false), eq((false)));
}
@Test
@@ -3459,11 +3464,12 @@
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+ assertTrue(mService.isCallerInstantApp(45770, 0));
info.privateFlags = 0;
- assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+ assertFalse(mService.isCallerInstantApp(575370, 0));
}
@Test
@@ -3472,8 +3478,9 @@
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+ assertTrue(mService.isCallerInstantApp(68638450, 10));
}
@Test
@@ -3689,4 +3696,38 @@
new TestableToastCallback(), 2000, 0);
assertEquals(1, mService.mToastQueue.size());
}
+
+ @Test
+ public void testOnNotificationSmartReplySent() {
+ final int replyIndex = 2;
+ final String reply = "Hello";
+ final boolean generatedByAssistant = true;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationSmartReplySent(
+ r.getKey(), replyIndex, reply, generatedByAssistant);
+ verify(mAssistants).notifyAssistantSuggestedReplySent(
+ eq(r.sbn), eq(reply), eq(generatedByAssistant));
+ }
+
+ @Test
+ public void testOnNotificationActionClick() {
+ final int actionIndex = 2;
+ final Notification.Action action =
+ new Notification.Action.Builder(null, "text", null).build();
+ final boolean generatedByAssistant = false;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ NotificationVisibility notificationVisibility =
+ NotificationVisibility.obtain(r.getKey(), 1, 2, true);
+ mService.mNotificationDelegate.onNotificationActionClick(
+ 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
+ generatedByAssistant);
+ verify(mAssistants).notifyAssistantActionClicked(
+ eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 38d8e39..6c7ede3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -41,6 +41,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -1097,6 +1098,25 @@
assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name
}
+ @Test
+ public void testAddAutomaticZenRule() {
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ new ComponentName("android", "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+
+ assertTrue(id != null);
+ ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id);
+ assertTrue(ruleInConfig != null);
+ assertEquals(zenRule.isEnabled(), ruleInConfig.enabled);
+ assertEquals(zenRule.isModified(), ruleInConfig.modified);
+ assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId);
+ assertEquals(NotificationManager.zenModeFromInterruptionFilter(
+ zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode);
+ assertEquals(zenRule.getName(), ruleInConfig.name);
+ }
+
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
new file mode 100644
index 0000000..cdba9a1
--- /dev/null
+++ b/services/tests/wmtests/Android.bp
@@ -0,0 +1,52 @@
+//########################################################################
+// Build WmTests package
+//########################################################################
+
+android_test {
+ name: "WmTests",
+
+ // We only want this apk build for tests.
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "servicestests-utils",
+ "truth-prebuilt",
+ "testables",
+ "ub-uiautomator",
+ "hamcrest-library",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+
+}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
deleted file mode 100644
index 67c2860..0000000
--- a/services/tests/wmtests/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-#########################################################################
-# Build WmTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, ../servicestests/utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- services.core \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-extended-minus-junit4 \
- platform-test-annotations \
- truth-prebuilt \
- testables \
- ub-uiautomator \
- hamcrest-library
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.mock \
- android.test.base \
- android.test.runner \
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
-
-LOCAL_PACKAGE_NAME := WmTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f128b4e..3f3b996 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -34,9 +34,13 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
<application android:debuggable="true"
android:testOnly="true">
+ <uses-library android:name="android.test.mock" android:required="true" />
+
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index cb2a8ec..5bf3d2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -62,8 +62,9 @@
@Test
public void testLastFocusedStackIsUpdatedWhenMovingStack() {
// Create a stack at bottom.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack stack =
+ new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build();
final ActivityStack prevFocusedStack = display.getFocusedStack();
stack.moveToFront("moveStackToFront");
@@ -83,7 +84,7 @@
@Test
public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
// Create a pinned stack and move to front.
- final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
.setStack(pinnedStack).build();
@@ -96,7 +97,7 @@
// Create a fullscreen stack and move to front.
final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
- mSupervisor.getDefaultDisplay());
+ mRootActivityContainer.getDefaultDisplay());
fullscreenStack.moveToFront("moveFullscreenStackToFront");
// The focused stack should be the fullscreen stack.
@@ -138,7 +139,7 @@
final ActivityDisplay display = spy(createNewActivityDisplay());
doReturn(false).when(display).shouldDestroyContentOnRemove();
doReturn(true).when(display).supportsSystemDecorations();
- mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
// Put home stack on the display.
final ActivityStack homeStack = display.createStack(
@@ -175,14 +176,14 @@
*/
@Test
public void testTopRunningActivity() {
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = new StackBuilder(mSupervisor).build();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
final ActivityRecord activity = stack.getTopActivity();
// Create empty stack on top.
final ActivityStack emptyStack =
- new StackBuilder(mSupervisor).setCreateActivity(false).build();
+ new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
// Make sure the top running activity is not affected when keyguard is not locked.
assertTopRunningActivity(activity, display);
@@ -225,7 +226,7 @@
*/
@Test
public void testAlwaysOnTopStackLocation() {
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index c7f0521..cac9cf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -24,20 +24,30 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.timeout;
import android.content.Intent;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import java.util.Arrays;
/**
* Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -51,6 +61,7 @@
public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
private ActivityMetricsLogger mActivityMetricsLogger;
private ActivityMetricsLaunchObserver mLaunchObserver;
+ private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
private TestActivityStack mStack;
private TaskRecord mTask;
@@ -61,33 +72,62 @@
public void setUpAMLO() throws Exception {
setupActivityTaskManagerService();
- mActivityMetricsLogger =
- new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
-
mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
- // TODO: Use ActivityMetricsLaunchObserverRegistry .
- java.lang.reflect.Field f =
- mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
- f.setAccessible(true);
- f.set(mActivityMetricsLogger, mLaunchObserver);
+ // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
+ mActivityMetricsLogger = mSupervisor.getActivityMetricsLogger();
+
+ mLaunchObserverRegistry = mActivityMetricsLogger.getLaunchObserverRegistry();
+ mLaunchObserverRegistry.registerLaunchObserver(mLaunchObserver);
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
- mStack = mSupervisor.getDefaultDisplay().createStack(
+ mStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
}
+ @After
+ public void tearDownAMLO() throws Exception {
+ if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed.
+ mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
+ }
+ }
+
+ static class ActivityRecordMatcher implements ArgumentMatcher</*@ActivityRecordProto*/ byte[]> {
+ private final @ActivityRecordProto byte[] mExpected;
+
+ public ActivityRecordMatcher(ActivityRecord activityRecord) {
+ mExpected = activityRecordToProto(activityRecord);
+ }
+
+ public boolean matches(@ActivityRecordProto byte[] actual) {
+ return Arrays.equals(mExpected, actual);
+ }
+ }
+
+ static @ActivityRecordProto byte[] activityRecordToProto(ActivityRecord record) {
+ return ActivityMetricsLogger.convertActivityRecordToProto(record);
+ }
+
+ static @ActivityRecordProto byte[] eqProto(ActivityRecord record) {
+ return argThat(new ActivityRecordMatcher(record));
+ }
+
+ static <T> T verifyAsync(T mock) {
+ // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
+ return verify(mock, timeout(100));
+ }
+
@Test
public void testOnIntentStarted() throws Exception {
Intent intent = new Intent("action 1");
mActivityMetricsLogger.notifyActivityLaunching(intent);
- verify(mLaunchObserver).onIntentStarted(eq(intent));
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -102,7 +142,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
activityRecord);
- verify(mLaunchObserver).onIntentFailed();
+ verifyAsync(mLaunchObserver).onIntentFailed();
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -113,7 +153,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
mActivityRecord);
- verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -127,7 +167,7 @@
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
SystemClock.uptimeMillis());
- verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -135,12 +175,12 @@
public void testOnActivityLaunchCancelled() throws Exception {
testOnActivityLaunched();
- mActivityRecord.nowVisible = true;
+ mActivityRecord.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
- verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -151,7 +191,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
mActivityRecord);
- verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
// A second, distinct, activity launch is coalesced into the the current app launch sequence
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
@@ -170,7 +210,7 @@
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
SystemClock.uptimeMillis());
- verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -178,13 +218,26 @@
public void testOnActivityLaunchCancelledTrampoline() throws Exception {
testOnActivityLaunchedTrampoline();
- mActivityRecordTrampoline.nowVisible = true;
+ mActivityRecordTrampoline.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
mActivityRecordTrampoline);
- verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline));
verifyNoMoreInteractions(mLaunchObserver);
}
+
+ @Test
+ public void testActivityRecordProtoIsNotTooBig() throws Exception {
+ // The ActivityRecordProto must not be too big, otherwise converting it at runtime
+ // will become prohibitively expensive.
+ assertWithMessage("mActivityRecord: %s", mActivityRecord).
+ that(activityRecordToProto(mActivityRecord).length).
+ isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+
+ assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline).
+ that(activityRecordToProto(mActivityRecordTrampoline).length).
+ isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 170bd33..b6f1817 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,10 +29,9 @@
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static junit.framework.TestCase.assertNotNull;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -65,7 +64,7 @@
@Before
public void setUp() throws Exception {
setupActivityTaskManagerService();
- mStack = new StackBuilder(mSupervisor).build();
+ mStack = new StackBuilder(mRootActivityContainer).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
}
@@ -86,7 +85,7 @@
public void testStackCleanupOnTaskRemoval() {
mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
// Stack should be gone on task removal.
- assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
+ assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId));
}
@Test
@@ -116,7 +115,7 @@
assertFalse(pauseFound.value);
// Clear focused stack
- final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
when(display.getFocusedStack()).thenReturn(null);
// In the unfocused stack, the activity should move to paused.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 8a6d587..f7b5d26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -18,15 +18,8 @@
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
-import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -36,10 +29,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor
- .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.google.common.truth.Truth.assertThat;
@@ -54,12 +43,7 @@
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
-import android.app.ActivityOptions;
import android.app.WaitResult;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.graphics.Rect;
-import android.os.Build;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.MediumTest;
@@ -67,8 +51,6 @@
import org.junit.Before;
import org.junit.Test;
-import java.util.ArrayList;
-
/**
* Tests for the {@link ActivityStackSupervisor} class.
*
@@ -83,78 +65,11 @@
@Before
public void setUp() throws Exception {
setupActivityTaskManagerService();
- mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
/**
- * This test ensures that we do not try to restore a task based off an invalid task id. We
- * should expect {@code null} to be returned in this case.
- */
- @Test
- public void testRestoringInvalidTask() {
- ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
- TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
- assertNull(task);
- }
-
- /**
- * This test ensures that an existing task in the pinned stack is moved to the fullscreen
- * activity stack when a new task is added.
- */
- @Test
- public void testReplacingTaskInPinnedStack() {
- final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(mFullscreenStack).build();
- final TaskRecord firstTask = firstActivity.getTask();
-
- final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(mFullscreenStack).build();
- final TaskRecord secondTask = secondActivity.getTask();
-
- mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
-
- // Ensure full screen stack has both tasks.
- ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
-
- // Move first activity to pinned stack.
- final Rect sourceBounds = new Rect();
- mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
- 0f /*aspectRatio*/, "initialMove");
-
- final ActivityDisplay display = mFullscreenStack.getDisplay();
- ActivityStack pinnedStack = display.getPinnedStack();
- // Ensure a task has moved over.
- ensureStackPlacement(pinnedStack, firstTask);
- ensureStackPlacement(mFullscreenStack, secondTask);
-
- // Move second activity to pinned stack.
- mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
- 0f /*aspectRatio*/, "secondMove");
-
- // Need to get stacks again as a new instance might have been created.
- pinnedStack = display.getPinnedStack();
- mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- // Ensure stacks have swapped tasks.
- ensureStackPlacement(pinnedStack, secondTask);
- ensureStackPlacement(mFullscreenStack, firstTask);
- }
-
- private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
- final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
- assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
-
- if (tasks == null) {
- return;
- }
-
- for (TaskRecord task : tasks) {
- assertTrue(stackTasks.contains(task));
- }
- }
-
- /**
* Ensures that an activity is removed from the stopping activities list once it is resumed.
*/
@Test
@@ -179,7 +94,7 @@
// #notifyAll will be called on the ActivityManagerService. we must hold the object lock
// when this happens.
- synchronized (mSupervisor.mService.mGlobalLock) {
+ synchronized (mService.mGlobalLock) {
final WaitResult taskToFrontWait = new WaitResult();
mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
@@ -195,337 +110,7 @@
assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
- assertEquals(deliverToTopWait.who, firstActivity.realActivity);
+ assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent);
}
}
-
- @Test
- public void testApplySleepTokensLocked() {
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = mock(ActivityStack.class);
- display.addChild(stack, 0 /* position */);
-
- // Make sure we wake and resume in the case the display is turning on and the keyguard is
- // not showing.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- false /* keyguardShowing */, true /* expectWakeFromSleep */,
- true /* expectResumeTopActivity */);
-
- // Make sure we wake and don't resume when the display is turning on and the keyguard is
- // showing.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- true /* keyguardShowing */, true /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
-
- // Make sure we wake and don't resume when the display is turning on and the keyguard is
- // not showing as unfocused.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, false /* isFocusedStack */,
- false /* keyguardShowing */, true /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
-
- // Should not do anything if the display state hasn't changed.
- verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- false /* keyguardShowing */, false /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
- }
-
- private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
- ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
- boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
- boolean expectResumeTopActivity) {
- reset(stack);
-
- doReturn(displayShouldSleep).when(display).shouldSleep();
- doReturn(displaySleeping).when(display).isSleeping();
- doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
- doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
- doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
- mSupervisor.applySleepTokensLocked(true);
- verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
- verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
- null /* target */, null /* targetOptions */);
- }
-
- /**
- * Verifies that removal of activity with task and stack is done correctly.
- */
- @Test
- public void testRemovingStackOnAppCrash() {
- final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
- final int originalStackCount = defaultDisplay.getChildCount();
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
-
- assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
-
- // Let's pretend that the app has crashed.
- firstActivity.app.setThread(null);
- mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
-
- // Verify that the stack was removed.
- assertEquals(originalStackCount, defaultDisplay.getChildCount());
- }
-
- @Test
- public void testFocusability() {
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
-
- // Under split screen primary we should be focusable when not minimized
- mService.mStackSupervisor.setDockedStackMinimized(false);
- assertTrue(stack.isFocusable());
- assertTrue(activity.isFocusable());
-
- // Under split screen primary we should not be focusable when minimized
- mService.mStackSupervisor.setDockedStackMinimized(true);
- assertFalse(stack.isFocusable());
- assertFalse(activity.isFocusable());
-
- final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(pinnedStack).build();
-
- // We should not be focusable when in pinned mode
- assertFalse(pinnedStack.isFocusable());
- assertFalse(pinnedActivity.isFocusable());
-
- // Add flag forcing focusability.
- pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
- // We should not be focusable when in pinned mode
- assertTrue(pinnedStack.isFocusable());
- assertTrue(pinnedActivity.isFocusable());
-
- // Without the overridding activity, stack should not be focusable.
- pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
- REMOVE_TASK_MODE_DESTROYING);
- assertFalse(pinnedStack.isFocusable());
- }
-
- /**
- * Verify that split-screen primary stack will be chosen if activity is launched that targets
- * split-screen secondary, but a matching existing instance is found on top of split-screen
- * primary stack.
- */
- @Test
- public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
- // Create primary split-screen stack with a task and an activity.
- final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
- final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
-
- // Find a launch stack for the top activity in split-screen primary, while requesting
- // split-screen secondary.
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
- final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
-
- // Assert that the primary stack is returned.
- assertEquals(primaryStack, result);
- }
-
- /**
- * Verify split-screen primary stack & task can resized by
- * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
- */
- @Test
- public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect taskSize = new Rect(0, 0, 600, 600);
- final Rect stackSize = new Rect(0, 0, 300, 300);
-
- // Create primary split-screen stack with a task.
- final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-
- // Resize dock stack.
- mService.resizeDockedStack(stackSize, taskSize, null, null, null);
-
- // Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(primaryStack.getBounds(), stackSize);
- assertEquals(task.getBounds(), taskSize);
- }
-
- /**
- * Verify that home stack would be moved to front when the top activity is Recents.
- */
- @Test
- public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
- // Create stack/task on default display.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
- final TaskRecord targetTask = targetStack.getChildAt(0);
-
- // Create Recents on top of the display.
- final ActivityStack stack =
- new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
-
- final String reason = "findTaskToMoveToFront";
- mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
- false);
-
- verify(display).moveHomeStackToFront(contains(reason));
- }
-
- /**
- * Verify that home stack won't be moved to front if the top activity on other display is
- * Recents.
- */
- @Test
- public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
- // Create stack/task on default display.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-
- // Create Recents on secondary display.
- final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
- ActivityDisplay.POSITION_TOP);
- final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_RECENTS, true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
- new ActivityBuilder(mService).setTask(task).build();
-
- final String reason = "findTaskToMoveToFront";
- mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
- false);
-
- verify(display, never()).moveHomeStackToFront(contains(reason));
- }
-
- /**
- * Verify if a stack is not at the topmost position, it should be able to resume its activity if
- * the stack is the top focused.
- */
- @Test
- public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
- // Create a stack at bottom.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */));
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
- final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
- display.positionChildAtBottom(targetStack);
-
- // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
- // is the current top focused stack.
- assertFalse(targetStack.isTopStackOnDisplay());
- doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
-
- // Use the stack as target to resume.
- mSupervisor.resumeFocusedStacksTopActivitiesLocked(
- targetStack, activity, null /* targetOptions */);
-
- // Verify the target stack should resume its activity.
- verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
- eq(activity), eq(null /* targetOptions */));
- }
-
- /**
- * Tests home activities that targeted sdk before Q cannot start on secondary display.
- */
- @Test
- public void testStartHomeTargetSdkBeforeQ() throws Exception {
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondDisplay, POSITION_TOP);
- doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
- final ActivityInfo info = new ActivityInfo();
- info.launchMode = LAUNCH_MULTIPLE;
- info.applicationInfo = new ApplicationInfo();
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
-
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
- assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
- }
-
- /**
- * Tests that home activities can be started on the displays that supports system decorations.
- */
- @Test
- public void testStartHomeOnAllDisplays() {
- // Create secondary displays.
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondDisplay, POSITION_TOP);
- doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
- // Create mock tasks and other necessary mocks.
- TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
- final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
- TaskRecord.setTaskRecordFactory(factory);
- doAnswer(i -> taskBuilder.build()).when(factory)
- .create(any(), anyInt(), any(), any(), any(), any());
- doReturn(true).when(mService.mStackSupervisor)
- .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
- doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean());
-
- mSupervisor.startHomeOnAllDisplays(0, "testStartHome");
-
- assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome());
- assertNotNull(secondDisplay.getTopStack());
- assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
- }
-
- /**
- * Tests that home activities won't be started before booting when display added.
- */
- @Test
- public void testNotStartHomeBeforeBoot() {
- final int displayId = 1;
- final boolean isBooting = mService.mAmInternal.isBooting();
- final boolean isBooted = mService.mAmInternal.isBooted();
- try {
- mService.mAmInternal.setBooting(false);
- mService.mAmInternal.setBooted(false);
- mSupervisor.onDisplayAdded(displayId);
- verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
- } finally {
- mService.mAmInternal.setBooting(isBooting);
- mService.mAmInternal.setBooted(isBooted);
- }
- }
-
- /**
- * Tests whether home can be started if being instrumented.
- */
- @Test
- public void testCanStartHomeWhenInstrumented() {
- final ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- final WindowProcessController app = mock(WindowProcessController.class);
- doReturn(app).when(mService).getProcessController(any(), anyInt());
-
- // Can not start home if we don't want to start home while home is being instrumented.
- doReturn(true).when(app).isInstrumenting();
- assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- false /* allowInstrumenting*/));
-
- // Can start home for other cases.
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- true /* allowInstrumenting*/));
-
- doReturn(false).when(app).isInstrumenting();
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- false /* allowInstrumenting*/));
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- true /* allowInstrumenting*/));
- }
}
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 2fe45b8..f6871b3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -74,7 +74,7 @@
@Before
public void setUp() throws Exception {
setupActivityTaskManagerService();
- mDefaultDisplay = mSupervisor.getDefaultDisplay();
+ mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
true /* onTop */));
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
@@ -112,7 +112,7 @@
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
assertEquals(r, mStack.getResumedActivity());
- final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
@@ -130,7 +130,7 @@
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
assertEquals(r, mStack.getResumedActivity());
- final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
@@ -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());
@@ -233,14 +239,14 @@
.setStack(mStack)
.setUid(0)
.build();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
// Overlay must be for a different user to prevent recognizing a matching top activity
final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
.setUid(UserHandle.PER_USER_RANGE * 2).build();
taskOverlay.mTaskOverlay = true;
- final ActivityStackSupervisor.FindTaskResult result =
- new ActivityStackSupervisor.FindTaskResult();
+ final RootActivityContainer.FindTaskResult result =
+ new RootActivityContainer.FindTaskResult();
mStack.findTaskLocked(r, result);
assertEquals(r, task.getTopActivity(false /* includeOverlays */));
@@ -700,7 +706,7 @@
// should be destroyed immediately with updating configuration to restore original state.
final ActivityRecord activity1 = finishCurrentActivity(stack1);
assertEquals(DESTROYING, activity1.getState());
- verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+ verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
eq(display.mDisplayId), anyBoolean(), anyBoolean());
}
@@ -778,7 +784,7 @@
final ActivityDisplay display = mock(ActivityDisplay.class);
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
- doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
+ doReturn(display).when(mRootActivityContainer).getActivityDisplay(anyInt());
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index 9d93c85..2ba2fdb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -74,7 +74,7 @@
final ActivityRecord activity = new ActivityBuilder(mService).build();
final ActivityRecord source = new ActivityBuilder(mService).build();
final int startFlags = random.nextInt();
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final WindowProcessController wpc = new WindowProcessController(mService,
mService.mContext.getApplicationInfo(), "name", 12345,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 27fa20b..350114c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -90,6 +90,8 @@
@Mock
private ActivityTaskManagerService mService;
@Mock
+ private RootActivityContainer mRootActivityContainer;
+ @Mock
private ActivityStackSupervisor mSupervisor;
@Mock
private DevicePolicyManagerInternal mDevicePolicyManager;
@@ -111,7 +113,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mService.mAmInternal = mAmInternal;
- mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
+ mInterceptor = new ActivityStartInterceptor(
+ mService, mSupervisor, mRootActivityContainer, mContext);
mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
TEST_START_FLAGS, TEST_CALLING_PACKAGE);
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 50aa541..7c43cf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.START_ABORTED;
import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
@@ -69,6 +70,7 @@
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
@@ -110,6 +112,8 @@
private static final int FAKE_CALLING_UID = 666;
private static final int FAKE_REAL_CALLING_UID = 667;
private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
+ private static final int UNIMPORTANT_UID = 12345;
+ private static final int UNIMPORTANT_UID2 = 12346;
@Before
public void setUp() throws Exception {
@@ -125,18 +129,18 @@
public void testUpdateLaunchBounds() {
// When in a non-resizeable stack, the task bounds should be updated.
final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
- .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+ .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
.build();
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)
- .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+ .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
.build();
assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
@@ -147,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());
}
}
@@ -240,7 +244,7 @@
final ActivityRecord source = builder.build();
if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
- intent.setComponent(source.realActivity);
+ intent.setComponent(source.mActivityComponent);
}
if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
@@ -259,11 +263,11 @@
PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
doAnswer((inv) -> {
throw new RemoteException();
- }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
- any());
+ }).when(packageManager).activitySupportsIntent(
+ eq(source.mActivityComponent), eq(intent), any());
} else {
doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
- .when(packageManager).activitySupportsIntent(eq(source.realActivity),
+ .when(packageManager).activitySupportsIntent(eq(source.mActivityComponent),
eq(intent), any());
}
} catch (RemoteException e) {
@@ -314,7 +318,7 @@
* Creates a {@link ActivityStarter} with default parameters and necessary mocks.
*
* @param launchFlags The intent flags to launch activity.
- * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+ * @param mockGetLaunchStack Whether to mock {@link RootActivityContainer#getLaunchStack} for
* always launching to the testing stack. Set to false when allowing
* the activity can be launched to any stack that is decided by real
* implementation.
@@ -323,14 +327,14 @@
private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
boolean mockGetLaunchStack) {
// always allow test to start activity.
- doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
+ doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
anyBoolean(), anyBoolean(), any(), any(), any());
// instrument the stack and task used.
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+ final TaskRecord task = new TaskBuilder(mSupervisor)
.setCreateStack(false)
.build();
@@ -343,9 +347,9 @@
if (mockGetLaunchStack) {
// Direct starter to use spy stack.
- doReturn(stack).when(mService.mStackSupervisor)
+ doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean());
- doReturn(stack).when(mService.mStackSupervisor)
+ doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean(), any());
}
@@ -427,7 +431,7 @@
.setCreateTask(true)
.build();
- focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ActivityRecord reusableActivity = new ActivityBuilder(mService)
.setCreateTask(true)
@@ -435,13 +439,13 @@
// Create reusable activity after entering split-screen so that it is the top secondary
// stack.
- reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
// Set focus back to primary.
- final ActivityStack focusStack = focusActivity.getStack();
+ final ActivityStack focusStack = focusActivity.getActivityStack();
focusStack.moveToFront("testSplitScreenDeliverToTop");
- doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+ doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
@@ -464,16 +468,16 @@
.setCreateTask(true)
.build();
- reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final ActivityRecord focusActivity = new ActivityBuilder(mService)
.setCreateTask(true)
.build();
// Enter split-screen. Primary stack should have focus.
- focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+ doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
@@ -486,7 +490,7 @@
*/
@Test
public void testTaskModeViolation() {
- final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
((TestActivityDisplay) display).removeAllTasks();
assertNoTasks(display);
@@ -551,6 +555,123 @@
}
/**
+ * This test ensures that unsupported usecases aren't aborted when background starts are
+ * allowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsAllowed_noStartsAborted() {
+ doReturn(true).when(mService).isBackgroundActivityStartsEnabled();
+
+ runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ }
+
+ /**
+ * This test ensures that unsupported usecases are aborted when background starts are
+ * disallowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() {
+ doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_unsupportedUsecase_aborted", true,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed.
+ * The scenarios each have only one condidion that makes them supported.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() {
+ doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
+ Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
+ Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callingUidHasVisibleWindow_notAborted", false,
+ UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callingUidProcessStateTop_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_realCallingUidHasVisibleWindow_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_realCallingUidProcessStateTop_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_hasForegroundActivities_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ true, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callerIsRecents_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, true);
+ }
+
+ private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+ int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
+ int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
+ boolean hasForegroundActivities, boolean callerIsRecents) {
+ // window visibility
+ doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
+ callingUid);
+ doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
+ .isAnyWindowVisibleForUid(realCallingUid);
+ // process importance
+ doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
+ doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
+ // foreground activities
+ final IApplicationThread caller = mock(IApplicationThread.class);
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.uid = callingUid;
+ final WindowProcessController callerApp =
+ new WindowProcessController(mService, ai, null, callingUid, -1, null, null);
+ callerApp.setHasForegroundActivities(hasForegroundActivities);
+ doReturn(callerApp).when(mService).getProcessController(caller);
+ // caller is recents
+ RecentTasks recentTasks = mock(RecentTasks.class);
+ mService.mStackSupervisor.setRecentTasks(recentTasks);
+ doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
+
+ final ActivityOptions options = spy(ActivityOptions.makeBasic());
+ ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
+ .setCaller(caller)
+ .setCallingUid(callingUid)
+ .setRealCallingUid(realCallingUid)
+ .setActivityOptions(new SafeActivityOptions(options));
+
+ final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
+
+ assertEquals(ActivityStarter.getExternalResult(
+ shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
+ verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+ }
+
+ /**
* This test ensures that when starting an existing single task activity on secondary display
* which is not the top focused display, it should deliver new intent to the activity and not
* create a new stack.
@@ -562,7 +683,7 @@
// Create a secondary display at bottom.
final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
+ mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM);
final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -600,7 +721,7 @@
// Create a secondary display with an activity.
final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+ mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
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 caabdbd..3a56419 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -34,7 +34,7 @@
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.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -66,6 +66,8 @@
import com.android.server.AttributeCache;
import com.android.server.ServiceThread;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentController;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
@@ -91,6 +93,7 @@
final TestInjector mTestInjector = new TestInjector();
ActivityTaskManagerService mService;
+ RootActivityContainer mRootActivityContainer;
ActivityStackSupervisor mSupervisor;
// Default package name
@@ -115,27 +118,14 @@
}
ActivityTaskManagerService createActivityTaskManagerService() {
- final TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(mContext));
- setupActivityManagerService(atm);
- return atm;
+ mService = new TestActivityTaskManagerService(mContext);
+ mSupervisor = mService.mStackSupervisor;
+ mRootActivityContainer = mService.mRootActivityContainer;
+ return mService;
}
void setupActivityTaskManagerService() {
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
- }
-
- ActivityManagerService createActivityManagerService() {
- final TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(mContext));
- return setupActivityManagerService(atm);
- }
-
- ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
- final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
- setupActivityManagerService(am, atm);
- return am;
+ createActivityTaskManagerService();
}
/** Creates a {@link TestActivityDisplay}. */
@@ -150,36 +140,10 @@
/** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
TestActivityDisplay addNewActivityDisplayAt(int position) {
final TestActivityDisplay display = createNewActivityDisplay();
- mSupervisor.addChild(display, position);
+ mRootActivityContainer.addChild(display, position);
return display;
}
- void setupActivityManagerService(
- TestActivityManagerService am, TestActivityTaskManagerService atm) {
- atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
- atm.mAmInternal = am.getLocalService();
- am.mAtmInternal = atm.getLocalService();
- // Makes sure the supervisor is using with the spy object.
- atm.mStackSupervisor.setService(atm);
- doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
- doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
- PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
- doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
- doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
- doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- am.mActivityTaskManager = atm;
- am.mWindowManager = prepareMockWindowManager();
- atm.setWindowManager(am.mWindowManager);
-
- // Put a home stack on the default display, so that we'll always have something focusable.
- final TestActivityStackSupervisor supervisor =
- (TestActivityStackSupervisor) atm.mStackSupervisor;
- supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
- .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
- new ActivityBuilder(atm).setTask(task).build();
- }
-
/**
* Builder for creating new activities.
*/
@@ -268,7 +232,9 @@
aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
- activity.mWindowContainerController = mock(AppWindowContainerController.class);
+ spyOn(activity);
+ activity.mAppWindowToken = mock(AppWindowToken.class);
+ doNothing().when(activity).removeWindowContainer();
if (mTaskRecord != null) {
mTaskRecord.addActivityToTop(activity);
@@ -354,7 +320,7 @@
TaskRecord build() {
if (mStack == null && mCreateStack) {
- mStack = mSupervisor.getDefaultDisplay().createStack(
+ mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
@@ -405,23 +371,71 @@
}
}
- protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
- private LockTaskController mLockTaskController;
- private ActivityTaskManagerInternal mInternal;
+ protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
private PackageManagerInternal mPmInternal;
// ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
// We keep the reference in order to prevent creating it twice.
- private ActivityStackSupervisor mTestStackSupervisor;
+ ActivityStackSupervisor mTestStackSupervisor;
+
+ ActivityDisplay mDefaultDisplay;
TestActivityTaskManagerService(Context context) {
super(context);
+ spyOn(this);
+
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
+
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
mSupportsSplitScreenMultiWindow = true;
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
- mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+ final TestActivityManagerService am =
+ new TestActivityManagerService(mTestInjector, this);
+
+ spyOn(getLifecycleManager());
+ spyOn(getLockTaskController());
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ // allow background activity starts by default
+ doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+ }
+
+ void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
+ ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
+ mAmInternal = amInternal;
+ initialize(intentFirewall, intentController, looper);
+ initRootActivityContainerMocks(wm);
+ setWindowManager(wm);
+ }
+
+ void initRootActivityContainerMocks(WindowManagerService wm) {
+ spyOn(mRootActivityContainer);
+ mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
+ mRootActivityContainer.mWindowManager = wm;
+ mRootActivityContainer.mDisplayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ doNothing().when(mRootActivityContainer).setWindowManager(any());
+ // Invoked during {@link ActivityStack} creation.
+ doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+ // Always keep things awake.
+ doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+ // Called when moving activity to pinned stack.
+ doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+ anyBoolean());
+
+ // 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);
+ spyOn(mDefaultDisplay);
+ mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
+ mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final TaskRecord task = new TaskBuilder(mStackSupervisor)
+ .setStack(mDefaultDisplay.getHomeStack()).build();
+ new ActivityBuilder(this).setTask(task).build();
+
+ doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
}
@Override
@@ -430,54 +444,17 @@
}
@Override
- public LockTaskController getLockTaskController() {
- if (mLockTaskController == null) {
- mLockTaskController = spy(super.getLockTaskController());
- }
-
- return mLockTaskController;
- }
-
- @Override
void updateUsageStats(ActivityRecord component, boolean resumed) {
}
@Override
- protected final ActivityStackSupervisor createStackSupervisor() {
+ protected ActivityStackSupervisor createStackSupervisor() {
if (mTestStackSupervisor == null) {
- final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
- final KeyguardController keyguardController = mock(KeyguardController.class);
-
- // Invoked during {@link ActivityStack} creation.
- doNothing().when(supervisor).updateUIDsPresentOnDisplay();
- // Always keep things awake.
- doReturn(true).when(supervisor).hasAwakeDisplay();
- // Called when moving activity to pinned stack.
- doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
- anyBoolean());
- // Do not schedule idle timeouts
- doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
- // unit test version does not handle launch wake lock
- doNothing().when(supervisor).acquireLaunchWakelock();
- doReturn(keyguardController).when(supervisor).getKeyguardController();
-
- supervisor.initialize();
- mTestStackSupervisor = supervisor;
+ mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
}
return mTestStackSupervisor;
}
- protected ActivityStackSupervisor createTestSupervisor() {
- return new TestActivityStackSupervisor(this, mH.getLooper());
- }
-
- ActivityTaskManagerInternal getLocalService() {
- if (mInternal == null) {
- mInternal = new ActivityTaskManagerService.LocalService();
- }
- return mInternal;
- }
-
@Override
PackageManagerInternal getPackageManagerInternalLocked() {
if (mPmInternal == null) {
@@ -524,24 +501,31 @@
}
}
+ // TODO: Replace this with a mock object since we are no longer in AMS package.
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
*/
- static class TestActivityManagerService extends ActivityManagerService {
+ class TestActivityManagerService extends ActivityManagerService {
- private ActivityManagerInternal mInternal;
-
- TestActivityManagerService(TestInjector testInjector) {
+ TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
super(testInjector, testInjector.mHandlerThread);
- mUgmInternal = mock(UriGrantsManagerInternal.class);
- }
+ spyOn(this);
- ActivityManagerInternal getLocalService() {
- if (mInternal == null) {
- mInternal = new LocalService();
- }
- return mInternal;
+ mWindowManager = prepareMockWindowManager();
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+ atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
+ testInjector.mHandlerThread.getLooper());
+
+ mActivityTaskManager = atm;
+ mAtmInternal = atm.mInternal;
+
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+ doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
+ doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+ doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
}
}
@@ -549,24 +533,22 @@
* An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
* setup not available in the test environment. Also specifies an injector for
*/
- protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
- private ActivityDisplay mDisplay;
+ protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
private KeyguardController mKeyguardController;
- public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+ TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
super(service, looper);
- mDisplayManager =
- (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+ spyOn(this);
mWindowManager = prepareMockWindowManager();
mKeyguardController = mock(KeyguardController.class);
- setWindowContainerController(mock(RootWindowContainerController.class));
- }
- @Override
- public void initialize() {
- super.initialize();
- mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
- addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+ // Do not schedule idle timeouts
+ doNothing().when(this).scheduleIdleTimeoutLocked(any());
+ // unit test version does not handle launch wake lock
+ doNothing().when(this).acquireLaunchWakelock();
+ doReturn(mKeyguardController).when(this).getKeyguardController();
+
+ initialize();
}
@Override
@@ -575,11 +557,6 @@
}
@Override
- ActivityDisplay getDefaultDisplay() {
- return mDisplay;
- }
-
- @Override
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
}
@@ -596,7 +573,7 @@
DisplayInfo info) {
if (displayId == DEFAULT_DISPLAY) {
return new TestActivityDisplay(supervisor,
- supervisor.mDisplayManager.getDisplay(displayId));
+ supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
}
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
info, DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -604,7 +581,7 @@
}
TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
- super(supervisor, display);
+ super(supervisor.mService.mRootActivityContainer, display);
// Normally this comes from display-properties as exposed by WM. Without that, just
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -615,7 +592,7 @@
@Override
<T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
- return new StackBuilder(mSupervisor).setDisplay(this)
+ return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
.setWindowingMode(windowingMode).setActivityType(activityType)
.setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
}
@@ -757,8 +734,8 @@
}
}
- protected static class StackBuilder {
- private final ActivityStackSupervisor mSupervisor;
+ static class StackBuilder {
+ private final RootActivityContainer mRootActivityContainer;
private ActivityDisplay mDisplay;
private int mStackId = -1;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -766,9 +743,9 @@
private boolean mOnTop = true;
private boolean mCreateActivity = true;
- StackBuilder(ActivityStackSupervisor supervisor) {
- mSupervisor = supervisor;
- mDisplay = mSupervisor.getDefaultDisplay();
+ StackBuilder(RootActivityContainer root) {
+ mRootActivityContainer = root;
+ mDisplay = mRootActivityContainer.getDefaultDisplay();
}
StackBuilder setWindowingMode(int windowingMode) {
@@ -805,7 +782,8 @@
<T extends ActivityStack> T build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
if (mWindowingMode == WINDOWING_MODE_PINNED) {
- return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+ return (T) new PinnedActivityStack(mDisplay, stackId,
+ mRootActivityContainer.mStackSupervisor, mOnTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
@@ -821,7 +799,8 @@
}
};
} else {
- return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+ return (T) new TestActivityStack(mDisplay, stackId,
+ mRootActivityContainer.mStackSupervisor, mWindowingMode,
mActivityType, mOnTop, mCreateActivity);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index a907161..5556a15 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -19,12 +19,12 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.FlakyTest;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
similarity index 84%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f12619c..577859c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -24,18 +24,18 @@
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
-import android.view.IApplicationToken;
import androidx.test.filters.SmallTest;
@@ -111,16 +111,9 @@
final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- // Set TestAppWindowContainerController & assign first app token state to be good to go.
- final WindowTestUtils.TestAppWindowContainerController controller1 =
- createAppWindowController(dc1, token1.appToken);
- final WindowTestUtils.TestAppWindowContainerController controller2 =
- createAppWindowController(dc1, token2.appToken);
- controller1.setContainer(token1);
token1.allDrawn = true;
token1.startingDisplayed = true;
token1.startingMoved = true;
- controller2.setContainer(token2);
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
@@ -132,8 +125,8 @@
assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
// One activity window is visible for resuming & the other activity window is invisible
// for finishing in different display.
- controller1.setVisibility(true, false);
- controller2.setVisibility(false, false);
+ token1.setVisibility(true, false);
+ token2.setVisibility(false, false);
// Make sure each display is in animating stage.
assertTrue(dc1.mOpeningApps.size() > 0);
@@ -174,16 +167,4 @@
assertFalse(dc1.mOpeningApps.contains(token1));
}
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
- DisplayContent dc, IApplicationToken token) {
- return createAppWindowController(
- new WindowTestUtils.TestTaskWindowContainerController(
- createStackControllerOnDisplay(dc)), token);
- }
-
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
- WindowTestUtils.TestTaskWindowContainerController taskController,
- IApplicationToken token) {
- return new WindowTestUtils.TestAppWindowContainerController(taskController, token);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index 4522494..dcfb879 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -20,11 +20,12 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.SurfaceControl.Transaction;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
import android.view.SurfaceControl;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
similarity index 63%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 552390d..92b4dbb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -31,13 +32,17 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_UNSET;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
import android.graphics.Point;
import android.graphics.Rect;
@@ -66,6 +71,8 @@
Task mTask;
WindowTestUtils.TestAppWindowToken mToken;
+ private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
@Before
public void setUp() throws Exception {
mStack = createTaskStackOnDisplay(mDisplayContent);
@@ -112,7 +119,8 @@
assertEquals(window1, mToken.findMainWindow());
window1.mAnimatingExit = true;
assertEquals(window1, mToken.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
+ "window2");
assertEquals(window2, mToken.findMainWindow());
mToken.removeImmediately();
}
@@ -147,18 +155,18 @@
// 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.resizeReported = false;
+ 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.resizeReported);
+ assertTrue(appWindow.mResizeReported);
appWindow.removeImmediately();
}
@@ -179,11 +187,11 @@
// Set initial orientation and update.
performRotation(spiedRotation, Surface.ROTATION_90);
- appWindow.resizeReported = false;
+ appWindow.mResizeReported = false;
// Update the rotation to perform 180 degree rotation and check that resize was reported.
performRotation(spiedRotation, Surface.ROTATION_270);
- assertTrue(appWindow.resizeReported);
+ assertTrue(appWindow.mResizeReported);
appWindow.removeImmediately();
}
@@ -215,7 +223,8 @@
// Can not specify orientation if app isn't visible even though it fills parent.
assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
+ mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
}
@Test
@@ -241,7 +250,8 @@
// Finish relaunching and ensure flag is now not reported
mToken.finishRelaunching();
- assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
+ assertFalse(
+ mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
}
@Test
@@ -251,7 +261,7 @@
"closingWindow");
closingWindow.mAnimatingExit = true;
closingWindow.mRemoveOnExit = true;
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
// We pretended that we were running an exit animation, but that should have been cleared up
@@ -261,6 +271,124 @@
}
@Test
+ public void testSetOrientation() {
+ // Assert orientation is unspecified to start.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation());
+
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+
+ mDisplayContent.removeAppToken(mToken.token);
+ // Assert orientation is unset to after container is removed.
+ assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+
+ // Reset display frozen state
+ mWm.mDisplayFrozen = false;
+ }
+
+ @Test
+ public void testCreateRemoveStartingWindow() {
+ mToken.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ assertHasStartingWindow(mToken);
+ mToken.removeStartingWindow();
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(mToken);
+ }
+
+ @Test
+ public void testAddRemoveRace() {
+ // There was once a race condition between adding and removing starting windows
+ for (int i = 0; i < 1000; i++) {
+ final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken();
+
+ appToken.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ appToken.removeStartingWindow();
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(appToken);
+
+ appToken.getParent().getParent().removeImmediately();
+ }
+ }
+
+ @Test
+ public void testTransferStartingWindow() {
+ final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+ final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+ token1.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ token2.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(),
+ true, true, false, true, false, false);
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(token1);
+ assertHasStartingWindow(token2);
+ }
+
+ @Test
+ public void testTransferStartingWindowWhileCreating() {
+ final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+ final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+ ((TestWindowManagerPolicy) token1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+ () -> {
+ // Surprise, ...! Transfer window in the middle of the creation flow.
+ token2.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0,
+ token1.appToken.asBinder(), true, true, false,
+ true, false, false);
+ });
+ token1.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(token1);
+ assertHasStartingWindow(token2);
+ }
+
+ private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() {
+ final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(taskStack, 0 /* userId */);
+ return createTestAppWindowTokenForGivenTask(task);
+ }
+
+ private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
+ final WindowTestUtils.TestAppWindowToken appToken =
+ WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+ task.addChild(appToken, 0);
+ waitUntilHandlersIdle();
+ return appToken;
+ }
+
+ @Test
+ public void testTryTransferStartingWindowFromHiddenAboveToken() {
+ // Add two tasks on top of each other.
+ final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken();
+ final WindowTestUtils.TestAppWindowToken tokenBottom =
+ createTestAppWindowTokenForGivenTask(tokenTop.getTask());
+
+ // Add a starting window.
+ tokenTop.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+
+ // Make the top one invisible, and try transferring the starting window from the top to the
+ // bottom one.
+ tokenTop.setVisibility(false, false);
+ tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+
+ // Assert that the bottom window now has the starting window.
+ assertNoStartingWindow(tokenTop);
+ assertHasStartingWindow(tokenBottom);
+ }
+
+ @Test
public void testTransitionAnimationPositionAndBounds() {
final Rect stackBounds = new Rect(
0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */);
@@ -285,4 +413,19 @@
assertEquals(expectedY, outPosition.y);
assertEquals(expectedBounds, outBounds);
}
+
+ private void assertHasStartingWindow(AppWindowToken atoken) {
+ assertNotNull(atoken.startingSurface);
+ assertNotNull(atoken.startingData);
+ assertNotNull(atoken.startingWindow);
+ }
+
+ private void assertNoStartingWindow(AppWindowToken atoken) {
+ assertNull(atoken.startingSurface);
+ assertNull(atoken.startingWindow);
+ assertNull(atoken.startingData);
+ atoken.forAllWindows(windowState -> {
+ assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
+ }, true);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
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/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 991981f..ee1c8df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -16,16 +16,17 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -145,7 +146,7 @@
}
@Test
- public void testUpdateDimsAppliesSize() {
+ public void testUpdateDimsAppliesCrop() {
mDimmer.dimAbove(mTransaction, 0.8f);
int width = 100;
@@ -153,7 +154,7 @@
Rect bounds = new Rect(0, 0, width, height);
mDimmer.updateDims(mTransaction, bounds);
- verify(mTransaction).setSize(getDimLayer(), width, height);
+ verify(mTransaction).setWindowCrop(getDimLayer(), width, height);
verify(mTransaction).show(getDimLayer());
}
@@ -242,13 +243,13 @@
SurfaceControl dimLayer = getDimLayer();
bounds.set(0, 0, 10, 10);
mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
verify(mTransaction, times(1)).show(dimLayer);
- verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
verify(mTransaction).setPosition(dimLayer, 0, 0);
bounds.set(10, 10, 30, 30);
mDimmer.updateDims(mTransaction, bounds);
- verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
+ verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
verify(mTransaction).setPosition(dimLayer, 10, 10);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 43e10f0..7c83ecc 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -29,6 +29,10 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -39,10 +43,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
@@ -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/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 18bd2e4..6767465 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -28,14 +28,11 @@
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
new file mode 100644
index 0000000..845a09f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+@Presubmit
+public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
+
+ private DisplayFrames mFrames;
+ private WindowState mWindow;
+ private int mRotation = ROTATION_0;
+ private boolean mHasDisplayCutout;
+
+ @Before
+ public void setUp() throws Exception {
+ updateDisplayFrames();
+
+ mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
+ // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
+ // changing those frames.
+ doNothing().when(mWindow).computeFrameLw();
+
+ final WindowManager.LayoutParams attrs = mWindow.mAttrs;
+ attrs.width = MATCH_PARENT;
+ attrs.height = MATCH_PARENT;
+ attrs.flags =
+ FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ attrs.format = PixelFormat.TRANSLUCENT;
+ }
+
+ public void setRotation(int rotation) {
+ mRotation = rotation;
+ updateDisplayFrames();
+ }
+
+ public void addDisplayCutout() {
+ mHasDisplayCutout = true;
+ updateDisplayFrames();
+ }
+
+ private void updateDisplayFrames() {
+ final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
+ mHasDisplayCutout);
+ mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
+ }
+
+ @Test
+ public void addingWindow_doesNotTamperWithSysuiFlags() {
+ mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ assertEquals(0, mWindow.mAttrs.systemUiVisibility);
+ assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
+ }
+
+ @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() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ 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);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_never() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ addWindow(mWindow);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ 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_withDisplayCutout_fullscreen() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ addWindow(mWindow);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ 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);
+
+ 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() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+ addWindow(mWindow);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_seascape() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_270);
+ addWindow(mWindow);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
+ 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);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
+ 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);
+
+ 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);
+ }
+ }
+
+ @Test
+ public void layoutHint_appWindow() {
+ 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();
+
+ 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()));
+ }
+ }
+
+ @Test
+ public void layoutHint_appWindowInTask() {
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ 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();
+
+ 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()));
+ }
+ }
+
+ @Test
+ public void layoutHint_appWindowInTask_outsideContentFrame() {
+ 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);
+
+ 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);
+
+ 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()));
+ }
+ }
+
+ /**
+ * Asserts that {@code actual} is inset by the given amounts from the full display rect.
+ *
+ * Convenience wrapper for when only the top and bottom inset are non-zero.
+ */
+ private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
+ int expectedInsetBottom) {
+ assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
+ }
+
+ /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
+ private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
+ int expectedInsetRight, int expectedInsetBottom) {
+ assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
+ mFrames.mDisplayWidth - expectedInsetRight,
+ mFrames.mDisplayHeight - expectedInsetBottom), actual);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 07d5fea..8349ac7f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -42,12 +42,9 @@
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DisplayPolicyTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 1d63c57..7be331c 100644
--- a/services/tests/servicestests/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/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
new file mode 100644
index 0000000..e988994
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+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.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManagerInternal;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.view.Surface;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class for {@link DisplayRotation}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayRotationTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class DisplayRotationTests {
+ private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
+
+ private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
+
+ private WindowManagerService mMockWm;
+ private DisplayContent mMockDisplayContent;
+ private DisplayPolicy mMockDisplayPolicy;
+ private Context mMockContext;
+ private Resources mMockRes;
+ private SensorManager mMockSensorManager;
+ private Sensor mFakeSensor;
+ private DisplayWindowSettings mMockDisplayWindowSettings;
+ private ContentResolver mMockResolver;
+ private FakeSettingsProvider mFakeSettingsProvider;
+ private StatusBarManagerInternal mMockStatusBarManagerInternal;
+
+ // Fields below are callbacks captured from test target.
+ private ContentObserver mShowRotationSuggestionsObserver;
+ private ContentObserver mAccelerometerRotationObserver;
+ private ContentObserver mUserRotationObserver;
+ private SensorEventListener mOrientationSensorListener;
+
+ private DisplayRotationBuilder mBuilder;
+
+ private DisplayRotation mTarget;
+
+ @Before
+ public void setUp() {
+ FakeSettingsProvider.clearSettingsProvider();
+
+ mMockWm = mock(WindowManagerService.class);
+ mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+
+ mPreviousStatusBarManagerInternal = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+
+ mBuilder = new DisplayRotationBuilder();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ if (mPreviousStatusBarManagerInternal != null) {
+ LocalServices.addService(StatusBarManagerInternal.class,
+ mPreviousStatusBarManagerInternal);
+ mPreviousStatusBarManagerInternal = null;
+ }
+ }
+
+ // ================================
+ // Display Settings Related Tests
+ // ================================
+ @Test
+ public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception {
+ mBuilder.build();
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ assertEquals(0, Settings.System.getInt(mMockResolver,
+ Settings.System.ACCELEROMETER_ROTATION));
+ assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver,
+ Settings.System.USER_ROTATION));
+ }
+
+ @Test
+ public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.mIsDefaultDisplay = false;
+
+ mBuilder.build();
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent,
+ WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180);
+ }
+
+ @Test
+ public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception {
+ mBuilder.build();
+
+ thawRotation();
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+ assertEquals(1, Settings.System.getInt(mMockResolver,
+ Settings.System.ACCELEROMETER_ROTATION));
+ }
+
+ @Test
+ public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.mIsDefaultDisplay = false;
+
+ mBuilder.build();
+
+ thawRotation();
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+ verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent),
+ eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt());
+ }
+
+ @Test
+ public void testPersistsFixedToUserRotation() throws Exception {
+ mBuilder.build();
+
+ mTarget.setFixedToUserRotation(true);
+
+ verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true);
+
+ reset(mMockDisplayWindowSettings);
+ mTarget.setFixedToUserRotation(false);
+
+ verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false);
+ }
+
+ // ========================================
+ // Tests for User Rotation based Rotation
+ // ========================================
+ @Test
+ public void testReturnsUserRotation_UserRotationLocked_NoAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ final int rotation = mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90);
+ assertTrue("Rotation should be sideways, but it's "
+ + Surface.rotationToString(rotation),
+ rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ }
+
+ // =================================
+ // Tests for Sensor based Rotation
+ // =================================
+ private void verifyOrientationListenerRegistration(int numOfInvocation) {
+ final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
+ SensorEventListener.class);
+ verify(mMockSensorManager, times(numOfInvocation)).registerListener(
+ listenerCaptor.capture(),
+ same(mFakeSensor),
+ anyInt(),
+ any());
+ if (numOfInvocation > 0) {
+ mOrientationSensorListener = listenerCaptor.getValue();
+ }
+ }
+
+ @Test
+ public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception {
+ mBuilder.setSupportAutoRotation(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ScreenNotOn() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_NotAwake() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(false);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_FixedUserRotation() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.setFixedToUserRotation(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ private void enableOrientationSensor() {
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(1);
+ }
+
+ private SensorEvent createSensorEvent(int rotation) throws Exception {
+ final Constructor<SensorEvent> constructor =
+ SensorEvent.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ final SensorEvent event = constructor.newInstance(1);
+ event.sensor = mFakeSensor;
+ event.values[0] = rotation;
+ event.timestamp = SystemClock.elapsedRealtimeNanos();
+ return event;
+ }
+
+ @Test
+ public void testReturnsSensorRotation_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ private boolean waitForUiHandler() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ UiThread.getHandler().post(latch::countDown);
+ return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ @Test
+ public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertTrue(waitForUiHandler());
+
+ verify(mMockWm).updateRotation(false, false);
+ }
+
+ @Test
+ public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertTrue(waitForUiHandler());
+
+ verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+ }
+
+ @Test
+ public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+ final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE,
+ Surface.ROTATION_0);
+ assertTrue("Rotation should be sideways but it's "
+ + Surface.rotationToString(rotation),
+ rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ }
+
+ @Test
+ public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ // =================================
+ // Tests for Policy based Rotation
+ // =================================
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsLidOpenRotation_LidOpen() throws Exception {
+ mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getLidState()).thenReturn(
+ WindowManagerPolicy.WindowManagerFuncs.LID_OPEN);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testReturnsCarDockRotation_CarDockedMode() throws Exception {
+ mBuilder.setCarDockRotation(Surface.ROTATION_270).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception {
+ mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ mTarget.setFixedToUserRotation(true);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ final int rotation = mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
+ assertEquals(Surface.ROTATION_180, rotation);
+ }
+
+ @Test
+ public void testReturnsUserRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.setIsDefaultDisplay(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ /**
+ * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
+ * according to given parameters.
+ */
+ private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
+ final int width;
+ final int height;
+ switch (displayOrientation) {
+ case SCREEN_ORIENTATION_LANDSCAPE:
+ width = 1920;
+ height = 1080;
+ break;
+ case SCREEN_ORIENTATION_PORTRAIT:
+ width = 1080;
+ height = 1920;
+ break;
+ default:
+ throw new IllegalArgumentException("displayOrientation needs to be either landscape"
+ + " or portrait, but we got "
+ + ActivityInfo.screenOrientationToString(displayOrientation));
+ }
+
+ final PackageManager mockPackageManager = mock(PackageManager.class);
+ when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+ when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
+ .thenReturn(isCar);
+ when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ .thenReturn(isTv);
+
+ final int shortSizeDp = (isCar || isTv) ? 540 : 720;
+ final int longSizeDp = 960;
+ mTarget.configure(width, height, shortSizeDp, longSizeDp);
+ }
+
+ private void freezeRotation(int rotation) {
+ mTarget.freezeRotation(rotation);
+
+ if (mTarget.isDefaultDisplay) {
+ mAccelerometerRotationObserver.onChange(false);
+ mUserRotationObserver.onChange(false);
+ }
+ }
+
+ private void thawRotation() {
+ mTarget.thawRotation();
+
+ if (mTarget.isDefaultDisplay) {
+ mAccelerometerRotationObserver.onChange(false);
+ mUserRotationObserver.onChange(false);
+ }
+ }
+
+ private class DisplayRotationBuilder {
+ private boolean mIsDefaultDisplay = true;
+ private boolean mSupportAutoRotation = true;
+
+ private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+ private int mCarDockRotation;
+ private int mDeskDockRotation;
+ private int mUndockedHdmiRotation;
+
+ private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
+ mIsDefaultDisplay = isDefaultDisplay;
+ return this;
+ }
+
+ private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
+ mSupportAutoRotation = supportAutoRotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setLidOpenRotation(int rotation) {
+ mLidOpenRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setCarDockRotation(int rotation) {
+ mCarDockRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setDeskDockRotation(int rotation) {
+ mDeskDockRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) {
+ mUndockedHdmiRotation = rotation;
+ return this;
+ }
+
+ private void captureObservers() {
+ ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
+ ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mShowRotationSuggestionsObserver = captor.getValue();
+ }
+
+ captor = ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mAccelerometerRotationObserver = captor.getValue();
+ }
+
+ captor = ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mUserRotationObserver = captor.getValue();
+ }
+ }
+
+ private Sensor createSensor(int type) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+
+ setSensorType(sensor, type);
+ setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+ setSensorField(sensor, "mVendor", "Mock Vendor");
+ setSensorField(sensor, "mVersion", 1);
+ setSensorField(sensor, "mHandle", -1);
+ setSensorField(sensor, "mMaxRange", 10);
+ setSensorField(sensor, "mResolution", 1);
+ setSensorField(sensor, "mPower", 1);
+ setSensorField(sensor, "mMinDelay", 1000);
+ setSensorField(sensor, "mMaxDelay", 1000000000);
+ setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mId", -1);
+
+ return sensor;
+ }
+
+ private void setSensorType(Sensor sensor, int type) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ }
+
+ private void setSensorField(Sensor sensor, String fieldName, Object value)
+ throws Exception {
+ Field field = Sensor.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(sensor, value);
+ }
+
+ private int convertRotationToDegrees(@Surface.Rotation int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return 0;
+ case Surface.ROTATION_90:
+ return 90;
+ case Surface.ROTATION_180:
+ return 180;
+ case Surface.ROTATION_270:
+ return 270;
+ default:
+ return -1;
+ }
+ }
+
+ private void build() throws Exception {
+ mMockContext = mock(Context.class);
+
+ mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
+ mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+
+ mMockDisplayPolicy = mock(DisplayPolicy.class);
+
+ mMockRes = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn((mMockRes));
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
+ .thenReturn(mSupportAutoRotation);
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
+ .thenReturn(convertRotationToDegrees(mLidOpenRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation))
+ .thenReturn(convertRotationToDegrees(mCarDockRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation))
+ .thenReturn(convertRotationToDegrees(mDeskDockRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
+ .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
+
+ mMockSensorManager = mock(SensorManager.class);
+ when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
+ .thenReturn(mMockSensorManager);
+ mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+ when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
+ Collections.singletonList(mFakeSensor));
+
+ mMockResolver = mock(ContentResolver.class);
+ when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
+ mFakeSettingsProvider = new FakeSettingsProvider();
+ when(mMockResolver.acquireProvider(Settings.AUTHORITY))
+ .thenReturn(mFakeSettingsProvider.getIContentProvider());
+
+ mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
+ mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+ mMockDisplayWindowSettings, mMockContext, new Object());
+
+ captureObservers();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index b823e70..8e881b5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -21,13 +21,19 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+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.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Matchers.eq;
import android.app.WindowConfiguration;
import android.platform.test.annotations.Presubmit;
@@ -378,6 +384,33 @@
mSecondaryDisplay.getDisplayRotation().getUserRotation());
}
+ @Test
+ public void testNotFixedToUserRotationByDefault() {
+ mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_0);
+
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ mPrimaryDisplay = spy(mPrimaryDisplay);
+ when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+ mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+ verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false));
+ }
+
+ @Test
+ public void testSetFixedToUserRotation() {
+ mTarget.setFixedToUserRotation(mPrimaryDisplay, true);
+
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ mPrimaryDisplay = spy(mPrimaryDisplay);
+ when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+ applySettingsToDisplayByNewInstance(mPrimaryDisplay);
+
+ verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true));
+ }
+
private static void assertOverscan(DisplayContent display, int left, int top, int right,
int bottom) {
final DisplayInfo info = display.getDisplayInfo();
diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
index a04bf16..3206208 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
@@ -30,12 +30,9 @@
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DockedStackDividerControllerTests {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 55e766d..f1c6eab 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -20,13 +20,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+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.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.content.ClipData;
import android.graphics.PixelFormat;
@@ -171,7 +172,7 @@
try {
final SurfaceControl surface = new SurfaceControl.Builder(appSession)
.setName("drag surface")
- .setSize(100, 100)
+ .setBufferSize(100, 100)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
new file mode 100644
index 0000000..88215449
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -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.
+ */
+
+package com.android.server.wm;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsSourceProviderTest extends WindowTestsBase {
+
+ private InsetsSource mSource = new InsetsSource(TYPE_TOP_BAR);
+ private InsetsSourceProvider mProvider;
+
+ @Before
+ public void setUp() throws Exception {
+ mSource.setVisible(true);
+ mProvider = new InsetsSourceProvider(mSource,
+ mDisplayContent.getInsetsStateController(), mDisplayContent);
+ }
+
+ @Test
+ public void testPostLayout() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ topBar.mHasSurface = true;
+ mProvider.setWindow(topBar, null);
+ mProvider.onPostLayout();
+ assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0),
+ mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ }
+
+ @Test
+ public void testPostLayout_invisible() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar, null);
+ mProvider.onPostLayout();
+ assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ }
+
+ @Test
+ public void testPostLayout_frameProvider() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar,
+ (displayFrames, windowState, rect) -> {
+ rect.set(10, 10, 20, 20);
+ });
+ mProvider.onPostLayout();
+ assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
new file mode 100644
index 0000000..11526a8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsStateControllerTest extends WindowTestsBase {
+
+ @Test
+ public void testStripForDispatch_notOwn() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+ assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
+ }
+
+ @Test
+ public void testStripForDispatch_own() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+ .setWindow(topBar, null);
+ topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+ assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar));
+ }
+
+ @Test
+ public void testStripForDispatch_navBar() {
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
+ getController().getSourceProvider(TYPE_IME).setWindow(ime, null);
+ assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar));
+ }
+
+ @Test
+ public void testBarControllingWinChanged() {
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
+ getController().onBarControllingWindowChanged(app);
+ InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
+ assertEquals(2, controls.length);
+ }
+
+ @Test
+ public void testControlRevoked() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().onBarControllingWindowChanged(app);
+ assertNotNull(getController().getControlsForDispatch(app));
+ getController().onBarControllingWindowChanged(null);
+ assertNull(getController().getControlsForDispatch(app));
+ }
+
+ @Test
+ public void testControlRevoked_animation() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().onBarControllingWindowChanged(app);
+ assertNotNull(getController().getControlsForDispatch(app));
+ topBar.cancelAnimation();
+ assertNull(getController().getControlsForDispatch(app));
+ }
+
+ private InsetsStateController getController() {
+ return mDisplayContent.getInsetsStateController();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index fa4898b..3720c85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -89,9 +89,9 @@
final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
final ActivityOptions options = mock(ActivityOptions.class);
- mController.calculate(record.getTask(), layout, record, source, options,
+ mController.calculate(record.getTaskRecord(), layout, record, source, options,
new LaunchParams());
- verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
+ verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record),
eq(source), eq(options), any(), any());
}
@@ -114,7 +114,7 @@
mPersister.putLaunchParams(userId, name, expected);
- mController.calculate(activity.getTask(), null /*layout*/, activity, null /*source*/,
+ mController.calculate(activity.getTaskRecord(), null /*layout*/, activity, null /*source*/,
null /*options*/, new LaunchParams());
verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), eq(expected),
any());
@@ -228,7 +228,7 @@
final LaunchParams result = new LaunchParams();
final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
- vrActivity.requestedVrComponent = vrActivity.realActivity;
+ vrActivity.requestedVrComponent = vrActivity.mActivityComponent;
// VR activities should always land on default display.
mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
@@ -412,8 +412,9 @@
@Override
void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams params) {
- final int userId = task != null ? task.userId : activity.userId;
- final ComponentName name = task != null ? task.realActivity : activity.realActivity;
+ final int userId = task != null ? task.userId : activity.mUserId;
+ final ComponentName name = task != null
+ ? task.realActivity : activity.mActivityComponent;
params.reset();
final Map<ComponentName, LaunchParams> map = mMap.get(userId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index dc22bc1..f3a125b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -110,8 +110,9 @@
final DisplayInfo info = new DisplayInfo();
info.uniqueId = mDisplayUniqueId;
mTestDisplay = createNewActivityDisplay(info);
- mSupervisor.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
- when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(mTestDisplay);
+ mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
+ when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId)))
+ .thenReturn(mTestDisplay);
ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE,
ACTIVITY_TYPE_STANDARD, /* onTop */ true);
@@ -184,7 +185,7 @@
public void testReturnsEmptyDisplayIfDisplayIsNotFound() {
mTarget.saveTask(mTestTask);
- when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
+ when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
mTarget.getLaunchParams(mTestTask, null, mResult);
@@ -271,6 +272,51 @@
}
@Test
+ public void testClearsRecordInMemory() {
+ mTarget.saveTask(mTestTask);
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ mTarget.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testClearsWriteQueueItem() {
+ mTarget.saveTask(mTestTask);
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testClearsFile() {
+ mTarget.saveTask(mTestTask);
+ mPersisterQueue.flush();
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+
+ @Test
public void testClearsRecordInMemoryOnPackageUninstalled() {
mTarget.saveTask(mTestTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 33e6063..6259fa6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -104,6 +104,7 @@
new DexmakerShareClassLoaderRule();
@Mock private ActivityStackSupervisor mSupervisor;
+ @Mock private RootActivityContainer mRootActivityContainer;
@Mock private IDevicePolicyManager mDevicePolicyManager;
@Mock private IStatusBarService mStatusBarService;
@Mock private WindowManagerService mWindowManager;
@@ -129,6 +130,7 @@
}
mSupervisor.mRecentTasks = mRecentTasks;
+ mSupervisor.mRootActivityContainer = mRootActivityContainer;
mLockTaskController = new LockTaskController(mContext, mSupervisor,
new ImmediatelyExecuteHandler());
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 1fae317..63d9fb9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -18,14 +18,15 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -46,6 +47,8 @@
@Presubmit
public class PinnedStackControllerTest extends WindowTestsBase {
+ private static final int SHELF_HEIGHT = 300;
+
@Mock private IPinnedStackListener mIPinnedStackListener;
@Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub;
@@ -70,8 +73,6 @@
reset(mIPinnedStackListener);
- final int SHELF_HEIGHT = 300;
-
mWm.setShelfHeight(true, SHELF_HEIGHT);
verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
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 2f3f698..3c7b4b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -110,14 +110,13 @@
@Before
public void setUp() throws Exception {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mTestService = spy(new MyTestActivityTaskManagerService(mContext));
- final TestActivityManagerService am = spy(new MyTestActivityManagerService());
- setupActivityManagerService(am, mTestService);
+ mTestService = new MyTestActivityTaskManagerService(mContext);
mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
- mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+ mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
+ mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -868,20 +867,20 @@
}
@Override
- protected ActivityStackSupervisor createTestSupervisor() {
- return new MyTestActivityStackSupervisor(this, mH.getLooper());
- }
-
- }
-
- private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService() {
- super(mTestInjector);
+ protected ActivityStackSupervisor createStackSupervisor() {
+ if (mTestStackSupervisor == null) {
+ mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
+ }
+ return mTestStackSupervisor;
}
@Override
- public boolean isUserRunning(int userId, int flags) {
- return true;
+ void initRootActivityContainerMocks(WindowManagerService wm) {
+ super.initRootActivityContainerMocks(wm);
+ mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+ mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
+ mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
}
@@ -891,15 +890,6 @@
}
@Override
- public void initialize() {
- super.initialize();
- mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
- mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
- addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
- addChild(mDisplay, ActivityDisplay.POSITION_TOP);
- }
-
- @Override
RunningTasks createRunningTasks() {
mRunningTasks = new TestRunningTasks();
return mRunningTasks;
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index ee3bba7..cc6a58a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -21,6 +21,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -28,10 +32,6 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
import android.os.Binder;
import android.os.IInterface;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 50190e7..0ff67d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -25,6 +25,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
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.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -50,61 +51,50 @@
public class RecentsAnimationTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private TestActivityTaskManagerService mTestService;
private ComponentName mRecentsComponent;
@Before
public void setUp() throws Exception {
mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
- mTestService = spy(new MyTestActivityTaskManagerService(mContext));
- setupActivityManagerService(mTestService);
+ mService = new TestActivityTaskManagerService(mContext);
+
+ final RecentTasks recentTasks = mService.getRecentTasks();
+ spyOn(recentTasks);
+ mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+ doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
}
@Test
public void testCancelAnimationOnStackOrderChange() {
ActivityStack fullscreenStack =
- mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
- ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+ ActivityRecord recentsActivity = new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
.setCreateTask(true)
.setStack(recentsStack)
.build();
ActivityStack fullscreenStack2 =
- mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+ ActivityRecord fsActivity = new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
.setCreateTask(true)
.setStack(fullscreenStack2)
.build();
- doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+ doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
// Start the recents animation
Intent recentsIntent = new Intent();
recentsIntent.setComponent(mRecentsComponent);
- mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+ mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
fullscreenStack.moveToFront("Activity start");
// Ensure that the recents animation was canceled
- verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
eq(REORDER_KEEP_IN_PLACE), any());
}
-
- private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
- MyTestActivityTaskManagerService(Context context) {
- super(context);
- }
-
- @Override
- protected RecentTasks createRecentTasks() {
- RecentTasks recents = mock(RecentTasks.class);
- doReturn(mRecentsComponent).when(recents).getRecentsComponent();
- System.out.println(mRecentsComponent);
- return recents;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index fa53795..ad2a708 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -18,13 +18,14 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
import android.graphics.Point;
import android.graphics.Rect;
@@ -143,8 +144,9 @@
@Test
public void testTimeout_scaled() throws Exception {
mWm.setAnimationScale(2, 5.0f);
- try{
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ try {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+ "testWin");
final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
new Point(50, 100), new Rect(50, 100, 150, 150));
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
@@ -163,7 +165,6 @@
} finally {
mWm.setAnimationScale(2, 1.0f);
}
-
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
new file mode 100644
index 0000000..9b18388
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+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.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.MediumTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+public class RootActivityContainerTests extends ActivityTestsBase {
+ private ActivityStack mFullscreenStack;
+
+ @Before
+ public void setUp() throws Exception {
+ setupActivityTaskManagerService();
+ mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ }
+
+ /**
+ * This test ensures that we do not try to restore a task based off an invalid task id. We
+ * should expect {@code null} to be returned in this case.
+ */
+ @Test
+ public void testRestoringInvalidTask() {
+ ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+ TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+ assertNull(task);
+ }
+
+ /**
+ * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+ * activity stack when a new task is added.
+ */
+ @Test
+ public void testReplacingTaskInPinnedStack() {
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final TaskRecord firstTask = firstActivity.getTaskRecord();
+
+ final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final TaskRecord secondTask = secondActivity.getTaskRecord();
+
+ mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
+
+ // Ensure full screen stack has both tasks.
+ ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
+
+ // Move first activity to pinned stack.
+ final Rect sourceBounds = new Rect();
+ mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds,
+ 0f /*aspectRatio*/, "initialMove");
+
+ final ActivityDisplay display = mFullscreenStack.getDisplay();
+ ActivityStack pinnedStack = display.getPinnedStack();
+ // Ensure a task has moved over.
+ ensureStackPlacement(pinnedStack, firstTask);
+ ensureStackPlacement(mFullscreenStack, secondTask);
+
+ // Move second activity to pinned stack.
+ mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds,
+ 0f /*aspectRatio*/, "secondMove");
+
+ // Need to get stacks again as a new instance might have been created.
+ pinnedStack = display.getPinnedStack();
+ mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ // Ensure stacks have swapped tasks.
+ ensureStackPlacement(pinnedStack, secondTask);
+ ensureStackPlacement(mFullscreenStack, firstTask);
+ }
+
+ private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
+ final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+ assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+ if (tasks == null) {
+ return;
+ }
+
+ for (TaskRecord task : tasks) {
+ assertTrue(stackTasks.contains(task));
+ }
+ }
+
+ @Test
+ public void testApplySleepTokens() {
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final KeyguardController keyguard = mSupervisor.getKeyguardController();
+ final ActivityStack stack = mock(ActivityStack.class);
+ display.addChild(stack, 0 /* position */);
+
+ // Make sure we wake and resume in the case the display is turning on and the keyguard is
+ // not showing.
+ verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedStack */,
+ false /* keyguardShowing */, true /* expectWakeFromSleep */,
+ true /* expectResumeTopActivity */);
+
+ // Make sure we wake and don't resume when the display is turning on and the keyguard is
+ // showing.
+ verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedStack */,
+ true /* keyguardShowing */, true /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+
+ // Make sure we wake and don't resume when the display is turning on and the keyguard is
+ // not showing as unfocused.
+ verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+ false /* displayShouldSleep */, false /* isFocusedStack */,
+ false /* keyguardShowing */, true /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+
+ // Should not do anything if the display state hasn't changed.
+ verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedStack */,
+ false /* keyguardShowing */, false /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+ }
+
+ private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+ ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+ boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+ boolean expectResumeTopActivity) {
+ reset(stack);
+
+ doReturn(displayShouldSleep).when(display).shouldSleep();
+ doReturn(displaySleeping).when(display).isSleeping();
+ doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+ doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
+ doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
+ mRootActivityContainer.applySleepTokens(true);
+ verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+ verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+ null /* target */, null /* targetOptions */);
+ }
+
+ /**
+ * Verifies that removal of activity with task and stack is done correctly.
+ */
+ @Test
+ public void testRemovingStackOnAppCrash() {
+ final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+ final int originalStackCount = defaultDisplay.getChildCount();
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+
+ // Let's pretend that the app has crashed.
+ firstActivity.app.setThread(null);
+ mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+ // Verify that the stack was removed.
+ assertEquals(originalStackCount, defaultDisplay.getChildCount());
+ }
+
+ @Test
+ public void testFocusability() {
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ // Under split screen primary we should be focusable when not minimized
+ mRootActivityContainer.setDockedStackMinimized(false);
+ assertTrue(stack.isFocusable());
+ assertTrue(activity.isFocusable());
+
+ // Under split screen primary we should not be focusable when minimized
+ mRootActivityContainer.setDockedStackMinimized(true);
+ assertFalse(stack.isFocusable());
+ assertFalse(activity.isFocusable());
+
+ final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(pinnedStack).build();
+
+ // We should not be focusable when in pinned mode
+ assertFalse(pinnedStack.isFocusable());
+ assertFalse(pinnedActivity.isFocusable());
+
+ // Add flag forcing focusability.
+ pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+ // We should not be focusable when in pinned mode
+ assertTrue(pinnedStack.isFocusable());
+ assertTrue(pinnedActivity.isFocusable());
+
+ // Without the overridding activity, stack should not be focusable.
+ pinnedStack.removeTask(pinnedActivity.getTaskRecord(), "testFocusability",
+ REMOVE_TASK_MODE_DESTROYING);
+ assertFalse(pinnedStack.isFocusable());
+ }
+
+ /**
+ * Verify that split-screen primary stack will be chosen if activity is launched that targets
+ * split-screen secondary, but a matching existing instance is found on top of split-screen
+ * primary stack.
+ */
+ @Test
+ public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+ // Create primary split-screen stack with a task and an activity.
+ final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+ .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+ // Find a launch stack for the top activity in split-screen primary, while requesting
+ // split-screen secondary.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ final ActivityStack result =
+ mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */);
+
+ // Assert that the primary stack is returned.
+ assertEquals(primaryStack, result);
+ }
+
+ /**
+ * Verify split-screen primary stack & task can resized by
+ * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+ */
+ @Test
+ public void testResizeDockedStackForSplitScreenPrimary() {
+ final Rect taskSize = new Rect(0, 0, 600, 600);
+ final Rect stackSize = new Rect(0, 0, 300, 300);
+
+ // Create primary split-screen stack with a task.
+ final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+ .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+ // Resize dock stack.
+ mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+
+ // Verify dock stack & its task bounds if is equal as resized result.
+ assertEquals(primaryStack.getBounds(), stackSize);
+ assertEquals(task.getBounds(), taskSize);
+ }
+
+ /**
+ * Verify that home stack would be moved to front when the top activity is Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+ // Create stack/task on default display.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final TestActivityStack targetStack =
+ new StackBuilder(mRootActivityContainer).setOnTop(false).build();
+ final TaskRecord targetTask = targetStack.getChildAt(0);
+
+ // Create Recents on top of the display.
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
+ ACTIVITY_TYPE_RECENTS).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(display).moveHomeStackToFront(contains(reason));
+ }
+
+ /**
+ * Verify that home stack won't be moved to front if the top activity on other display is
+ * Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+ // Create stack/task on default display.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+ // Create Recents on secondary display.
+ final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+ ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ new ActivityBuilder(mService).setTask(task).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(display, never()).moveHomeStackToFront(contains(reason));
+ }
+
+ /**
+ * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+ * the stack is the top focused.
+ */
+ @Test
+ public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
+ // Create a stack at bottom.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+ display.positionChildAtBottom(targetStack);
+
+ // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+ // is the current top focused stack.
+ assertFalse(targetStack.isTopStackOnDisplay());
+ doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
+
+ // Use the stack as target to resume.
+ mRootActivityContainer.resumeFocusedStacksTopActivities(
+ targetStack, activity, null /* targetOptions */);
+
+ // Verify the target stack should resume its activity.
+ verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+ eq(activity), eq(null /* targetOptions */));
+ }
+
+ /**
+ * Tests home activities that targeted sdk before Q cannot start on secondary display.
+ */
+ @Test
+ public void testStartHomeTargetSdkBeforeQ() throws Exception {
+ final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+ doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+ final ActivityInfo info = new ActivityInfo();
+ info.launchMode = LAUNCH_MULTIPLE;
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+ false /* allowInstrumenting */));
+
+ info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+ assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+ false /* allowInstrumenting */));
+ }
+
+ /**
+ * Tests that home activities can be started on the displays that supports system decorations.
+ */
+ @Test
+ public void testStartHomeOnAllDisplays() {
+ // Create secondary displays.
+ final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+ doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+ // Create mock tasks and other necessary mocks.
+ TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
+ final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
+ TaskRecord.setTaskRecordFactory(factory);
+ doAnswer(i -> taskBuilder.build()).when(factory)
+ .create(any(), anyInt(), any(), any(), any(), any());
+ doReturn(true).when(mRootActivityContainer)
+ .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome");
+
+ assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome());
+ assertNotNull(secondDisplay.getTopStack());
+ assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
+ }
+
+ /**
+ * Tests that home activities won't be started before booting when display added.
+ */
+ @Test
+ public void testNotStartHomeBeforeBoot() {
+ final int displayId = 1;
+ final boolean isBooting = mService.mAmInternal.isBooting();
+ final boolean isBooted = mService.mAmInternal.isBooted();
+ try {
+ mService.mAmInternal.setBooting(false);
+ mService.mAmInternal.setBooted(false);
+ mRootActivityContainer.onDisplayAdded(displayId);
+ verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+ } finally {
+ mService.mAmInternal.setBooting(isBooting);
+ mService.mAmInternal.setBooted(isBooted);
+ }
+ }
+
+ /**
+ * Tests whether home can be started if being instrumented.
+ */
+ @Test
+ public void testCanStartHomeWhenInstrumented() {
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ final WindowProcessController app = mock(WindowProcessController.class);
+ doReturn(app).when(mService).getProcessController(any(), anyInt());
+
+ // Can not start home if we don't want to start home while home is being instrumented.
+ doReturn(true).when(app).isInstrumenting();
+ assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ false /* allowInstrumenting*/));
+
+ // Can start home for other cases.
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ true /* allowInstrumenting*/));
+
+ doReturn(false).when(app).isInstrumenting();
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ false /* allowInstrumenting*/));
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ true /* allowInstrumenting*/));
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 0e1624e..a8b6dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -63,7 +63,7 @@
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
final ActivityStack stack =
- new StackBuilder(mSupervisor).setCreateActivity(false).build();
+ new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
display.addChild(stack, POSITION_BOTTOM);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 584f269..83e7ee7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -16,16 +16,17 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
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.Mockito.any;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import static java.util.concurrent.TimeUnit.SECONDS;
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6833dc5..d14f30d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -16,16 +16,17 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
@@ -225,11 +226,9 @@
mTransaction = transaction;
mParent = wm.makeSurfaceBuilder(mSession)
.setName("test surface parent")
- .setSize(3000, 3000)
.build();
mSurface = wm.makeSurfaceBuilder(mSession)
.setName("test surface")
- .setSize(1, 1)
.build();
mFinishedCallbackCalled = false;
mLeash = null;
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 6638eeb..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;
@@ -175,7 +178,7 @@
WINDOWING_MODE_FREEFORM);
ActivityRecord source = createSourceActivity(freeformDisplay);
- assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTask(), null /* layout */,
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTaskRecord(), null /* layout */,
null /* activity */, null /* source */, null /* options */, mCurrent, mResult));
assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
@@ -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 = mSupervisor.getOverrideConfiguration();
+ final Configuration overrideConfig =
+ mRootActivityContainer.getRequestedOverrideConfiguration();
// Egyptian Arabic is a RTL language.
overrideConfig.setLayoutDirection(new Locale("ar", "EG"));
- mSupervisor.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/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 785b955..b996bfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -52,8 +52,8 @@
private static final boolean DEBUGGING = false;
private static final String TAG = "TaskPositionerTest";
- private final static int MOUSE_DELTA_X = 5;
- private final static int MOUSE_DELTA_Y = 5;
+ private static final int MOUSE_DELTA_X = 5;
+ private static final int MOUSE_DELTA_Y = 5;
private int mMinVisibleWidth;
private int mMinVisibleHeight;
@@ -315,7 +315,7 @@
// Drag all the way to the right and see the height also shrinking.
mPositioner.resizeDrag(2000.0f, midY);
final int w = mMinVisibleWidth;
- final int h = Math.round((float)w / MIN_ASPECT);
+ final int h = Math.round((float) w / MIN_ASPECT);
assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
mPositioner.getWindowDragBounds());
@@ -428,7 +428,7 @@
// Drag all the way to the right.
mPositioner.resizeDrag(2000.0f, midY);
w = mMinVisibleWidth;
- h = Math.max(Math.round((float)w * MIN_ASPECT), r.height());
+ h = Math.max(Math.round((float) w * MIN_ASPECT), r.height());
assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
mPositioner.getWindowDragBounds());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 00b4629..3991e06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -18,14 +18,15 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import android.view.InputChannel;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index d2c0765..792e8a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -48,7 +48,7 @@
public void testGetClosingApps_closing() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mAppToken);
@@ -64,9 +64,9 @@
"closingWindow");
final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
FIRST_APPLICATION_WINDOW, "openingWindow");
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
- openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET,
+ openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mAppToken);
@@ -79,7 +79,7 @@
public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mAppToken);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index a569b9e..624ef9b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.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.server.wm;
@@ -20,14 +20,15 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 1af79e4..bbf508d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
@@ -37,6 +35,7 @@
@Presubmit
public class TaskWindowContainerControllerTests extends WindowTestsBase {
+ /* Comment out due to removal of AppWindowContainerController
@Test
public void testRemoveContainer() {
final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -49,7 +48,9 @@
assertNull(taskController.mContainer);
assertNull(appController.mContainer);
}
+ */
+ /* Comment out due to removal of AppWindowContainerController
@Test
public void testRemoveContainer_deferRemoval() {
final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -74,6 +75,7 @@
assertNull(appController.mContainer);
assertNull(app.getController());
}
+ */
@Test
public void testReparent() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
rename to services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 99deeb9..29738ff 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -24,6 +24,8 @@
import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.IWindow;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
@@ -41,6 +43,15 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)
+ throws RemoteException {
+ }
+
+ @Override
public void moved(int newX, int newY) throws RemoteException {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
rename to services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 7b542cb..ba81bd1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -19,8 +19,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import android.annotation.Nullable;
import android.content.Context;
@@ -46,7 +46,8 @@
class TestWindowManagerPolicy implements WindowManagerPolicy {
private final Supplier<WindowManagerService> mWmSupplier;
- boolean keyguardShowingAndNotOccluded = false;
+ int mRotationToReport = 0;
+ boolean mKeyguardShowingAndNotOccluded = false;
private Runnable mRunnableWhenAddingSplashScreen;
@@ -236,7 +237,7 @@
@Override
public boolean isKeyguardLocked() {
- return keyguardShowingAndNotOccluded;
+ return mKeyguardShowingAndNotOccluded;
}
@Override
@@ -256,7 +257,7 @@
@Override
public boolean isKeyguardShowingAndNotOccluded() {
- return keyguardShowingAndNotOccluded;
+ return mKeyguardShowingAndNotOccluded;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 9e22c0a..612f9ad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -73,7 +73,7 @@
public void testClear() {
final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
- mDisplayContent.mUnknownAppVisibilityController.clear();;
+ mDisplayContent.mUnknownAppVisibilityController.clear();
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 4ea6b39..d07230e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -18,11 +18,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
import android.os.IBinder;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 3643457..885a7e0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -231,7 +231,7 @@
final Configuration config = new Configuration();
final WindowConfiguration winConfig = config.windowConfiguration;
- stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
+ stackController.adjustConfigurationForBounds(bounds,
new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
windowingMode);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
index 7592f1c..de3567e 100644
--- a/services/tests/servicestests/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/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index e59afd6..60f957f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,13 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -30,13 +37,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -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);
}
@@ -459,13 +459,13 @@
@Test
public void testGetOrientation_childSpecified() {
testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
- SCREEN_ORIENTATION_LANDSCAPE);
+ SCREEN_ORIENTATION_LANDSCAPE);
testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
- SCREEN_ORIENTATION_UNSPECIFIED);
+ SCREEN_ORIENTATION_UNSPECIFIED);
}
private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
- int expectedOrientation) {
+ int expectedOrientation) {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
root.setFillsParent(true);
@@ -704,7 +704,7 @@
final TestWindowContainer root = builder.build();
final TestWindowContainer child = root.addChildWindow();
- child.setBounds(new Rect(1,1,2,2));
+ child.setBounds(new Rect(1, 1, 2, 2));
final TestWindowContainer grandChild = mock(TestWindowContainer.class);
@@ -742,7 +742,7 @@
private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> {
final int layer1 = w1.mLayer;
final int layer2 = w2.mLayer;
- if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
+ if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0)) {
// We insert the child window into the list ordered by the mLayer. For same layers,
// the negative one should go below others; the positive one should go above others.
return -1;
@@ -782,7 +782,7 @@
}
TestWindowContainer addChildWindow() {
- return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1));
+ return addChildWindow(new TestWindowContainerBuilder(mWmService).setLayer(1));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index fcde08e..4b666f5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -21,9 +21,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import android.platform.test.annotations.Presubmit;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 227eb00..d9ef10d 100644
--- a/services/tests/servicestests/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() {
@@ -290,7 +290,7 @@
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
w.computeFrameLw();
- assertFrame(w, 700, 0, 1000, 300);
+ assertFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
w.computeFrameLw();
assertFrame(w, 700, 700, 1000, 1000);
@@ -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/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
similarity index 86%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 9a13efb..50fd188 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -21,14 +21,16 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+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.mock;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -100,6 +102,7 @@
mock(PowerManagerInternal.class));
final PowerManagerInternal pm =
LocalServices.getService(PowerManagerInternal.class);
+ doNothing().when(pm).registerLowPowerModeObserver(any());
PowerSaveState state = new PowerSaveState.Builder().build();
doReturn(state).when(pm).getLowPowerState(anyInt());
@@ -123,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));
@@ -146,8 +150,8 @@
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.
+ // Display creation is driven by the ActivityManagerService via
+ // ActivityStackSupervisor. We emulate those steps here.
mService.mRoot.createDisplayContent(display, dcw);
}
@@ -171,21 +175,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/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 118ce89..7f78034 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -32,6 +32,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
@@ -43,11 +49,6 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
import android.graphics.Insets;
import android.graphics.Matrix;
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 e56edab..aa0ecf8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+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;
@@ -23,12 +26,24 @@
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;
+import android.view.IApplicationToken;
+import android.view.IWindow;
import android.view.Surface;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import org.mockito.invocation.InvocationOnMock;
@@ -37,6 +52,7 @@
* to WindowManager related test functionality.
*/
public class WindowTestUtils {
+ private static int sNextTaskId = 0;
/** An extension of {@link DisplayContent} to gain package scoped access. */
public static class TestDisplayContent extends DisplayContent {
@@ -54,6 +70,14 @@
return null;
}
+ /**
+ * Stubbing method of non-public parent class isn't supported, so here explicitly overrides.
+ */
+ @Override
+ DockedStackDividerController getDockedDividerController() {
+ return null;
+ }
+
/** Create a mocked default {@link DisplayContent}. */
public static TestDisplayContent create(Context context) {
final TestDisplayContent displayContent = mock(TestDisplayContent.class);
@@ -65,7 +89,7 @@
final DisplayRotation displayRotation = new DisplayRotation(
mock(WindowManagerService.class), displayContent, displayPolicy,
- context, new Object());
+ mock(DisplayWindowSettings.class), context, new Object());
displayRotation.mPortraitRotation = Surface.ROTATION_0;
displayRotation.mLandscapeRotation = Surface.ROTATION_90;
displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
@@ -96,16 +120,27 @@
// 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 Configuration config = invocationOnMock.<Configuration>getArgument(6);
final Rect bounds = invocationOnMock.<Rect>getArgument(0);
config.windowConfiguration.setBounds(bounds);
return null;
- }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
+ }).when(controller).adjustConfigurationForBounds(any(), any(), any(),
anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
return controller;
}
+ /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
+ public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
+ int userId) {
+ synchronized (service.mGlobalLock) {
+ final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
+ new ActivityManager.TaskDescription(), null);
+ stack.addTask(newTask, POSITION_TOP);
+ return newTask;
+ }
+ }
+
/**
* An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
* normally be mocked out.
@@ -120,4 +155,233 @@
// Do nothing.
}
}
+
+ static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
+ synchronized (dc.mWmService.mGlobalLock) {
+ return new TestAppWindowToken(dc);
+ }
+ }
+
+ /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
+ public static class TestAppWindowToken extends AppWindowToken {
+ boolean mOnTop = false;
+ private Transaction mPendingTransactionOverride;
+
+ private TestAppWindowToken(DisplayContent dc) {
+ super(dc.mWmService, new IApplicationToken.Stub() {
+ @Override
+ public String getName() {
+ return null;
+ }
+ }, new ComponentName("", ""), false, dc, true /* fillsParent */);
+ }
+
+ TestAppWindowToken(WindowManagerService service, IApplicationToken token,
+ ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
+ long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
+ int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
+ boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) {
+ super(service, token, activityComponent, voiceInteraction, dc,
+ inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
+ orientation, rotationAnimationHint, configChanges, launchTaskBehind,
+ alwaysFocusable, activityRecord);
+ }
+
+ int getWindowsCount() {
+ return mChildren.size();
+ }
+
+ boolean hasWindow(WindowState w) {
+ return mChildren.contains(w);
+ }
+
+ WindowState getFirstChild() {
+ return mChildren.peekFirst();
+ }
+
+ WindowState getLastChild() {
+ return mChildren.peekLast();
+ }
+
+ int positionInParent() {
+ return getParent().mChildren.indexOf(this);
+ }
+
+ void setIsOnTop(boolean onTop) {
+ mOnTop = onTop;
+ }
+
+ @Override
+ boolean isOnTop() {
+ return mOnTop;
+ }
+
+ void setPendingTransaction(Transaction transaction) {
+ mPendingTransactionOverride = transaction;
+ }
+
+ @Override
+ public Transaction getPendingTransaction() {
+ return mPendingTransactionOverride == null
+ ? super.getPendingTransaction()
+ : mPendingTransactionOverride;
+ }
+ }
+
+ static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
+ return createTestWindowToken(type, dc, false /* persistOnEmpty */);
+ }
+
+ static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
+ boolean persistOnEmpty) {
+ synchronized (dc.mWmService.mGlobalLock) {
+ return new TestWindowToken(type, dc, persistOnEmpty);
+ }
+ }
+
+ /* Used so we can gain access to some protected members of the {@link WindowToken} class */
+ public static class TestWindowToken extends WindowToken {
+
+ private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
+ super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
+ false /* ownerCanManageAppTokens */);
+ }
+
+ int getWindowsCount() {
+ return mChildren.size();
+ }
+
+ boolean hasWindow(WindowState w) {
+ return mChildren.contains(w);
+ }
+ }
+
+ /* Used so we can gain access to some protected members of the {@link Task} class */
+ public static class TestTask extends Task {
+ boolean mShouldDeferRemoval = false;
+ boolean mOnDisplayChangedCalled = false;
+ private boolean mIsAnimating = false;
+
+ TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
+ int resizeMode, boolean supportsPictureInPicture,
+ TaskWindowContainerController controller) {
+ super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
+ new ActivityManager.TaskDescription(), controller);
+ }
+
+ boolean shouldDeferRemoval() {
+ return mShouldDeferRemoval;
+ }
+
+ int positionInParent() {
+ return getParent().mChildren.indexOf(this);
+ }
+
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ mOnDisplayChangedCalled = true;
+ }
+
+ @Override
+ boolean isSelfAnimating() {
+ return mIsAnimating;
+ }
+
+ void setLocalIsAnimating(boolean isAnimating) {
+ mIsAnimating = isAnimating;
+ }
+ }
+
+ /**
+ * 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 class TestIApplicationToken implements IApplicationToken {
+
+ private final Binder mBinder = new Binder();
+ @Override
+ public IBinder asBinder() {
+ return mBinder;
+ }
+ @Override
+ public String getName() {
+ return null;
+ }
+ }
+
+ /** Used to track resize reports. */
+ public static class TestWindowState extends WindowState {
+ boolean mResizeReported;
+
+ TestWindowState(WindowManagerService service, Session session, IWindow window,
+ WindowManager.LayoutParams attrs, WindowToken token) {
+ super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
+ false /* ownerCanAddInternalSystemWindow */);
+ }
+
+ @Override
+ void reportResized() {
+ super.reportResized();
+ mResizeReported = true;
+ }
+
+ @Override
+ public boolean isGoneForLayoutLw() {
+ return false;
+ }
+
+ @Override
+ void updateResizingWindowIfNeeded() {
+ // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
+ // the system that it can actually update the window.
+ boolean hadSurface = mHasSurface;
+ mHasSurface = true;
+
+ super.updateResizingWindowIfNeeded();
+
+ mHasSurface = hadSurface;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1eb46fb..4394f9a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -37,7 +37,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.mockito.Mockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import android.content.Context;
import android.content.res.Configuration;
@@ -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;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 152831f..4f573a4 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,8 +27,6 @@
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -846,8 +844,6 @@
// Inform listeners if necessary
if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
|| event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -900,10 +896,6 @@
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.FOREGROUND_SERVICE_START:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
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/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d940620..01e566c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,6 +20,7 @@
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.util.XmlUtils;
@@ -89,11 +90,23 @@
// Apply the offset to the beginTime to find the absolute time.
stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_ACTIVE_ATTR);
- stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
- parser, LAST_TIME_SERVICE_USED_ATTR);
+
+ try {
+ stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_SERVICE_USED_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+ }
+
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
- stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
+
+ try {
+ stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
TOTAL_TIME_SERVICE_USED_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+ }
+
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR,
0);
@@ -350,8 +363,17 @@
}
statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
- statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
- statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+ try {
+ statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse majorVersion", e);
+ }
+
+ try {
+ statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse minorVersion", e);
+ }
int eventCode;
int outerDepth = parser.getDepth();
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 0c40a6b..de40e0d 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -24,7 +24,6 @@
"libdexfile",
"slicer",
],
- cppflags: ["-std=c++17"],
}
cc_library_host_static {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index cef99865..d617de0 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;
@@ -908,10 +907,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/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d97f0c5..143b323 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -304,4 +304,10 @@
* @see TelecomServiceImpl#handleCallIntent
*/
void handleCallIntent(in Intent intent);
+
+ void setTestDefaultCallScreeningApp(String packageName);
+
+ void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
+
+ void setTestAutoModeApp(String packageName);
}
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..6a396ce 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 {}
+
}
/**
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 5ad7c30..0a60e34 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1154,20 +1154,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";
/**
@@ -1838,6 +1851,13 @@
"notify_international_call_on_wfc_bool";
/**
+ * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN,
+ * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN.
+ * Default value is false.
+ */
+ public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
+
+ /**
* Flag specifying whether to show an alert dialog for video call charges.
* By default this value is {@code false}.
* @hide
@@ -2341,6 +2361,32 @@
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;
@@ -2526,6 +2572,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, "");
@@ -2643,6 +2690,7 @@
sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
false);
sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
null);
@@ -2700,6 +2748,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/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index e373797..269cda1 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -37,6 +37,7 @@
import android.util.ArraySet;
import android.util.Log;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -107,14 +108,14 @@
* {@link MbmsGroupCallSession} that you received before calling this method again.
*
* @param context The {@link Context} to use.
- * @param executor The executor on which you wish to execute callbacks.
* @param subscriptionId The subscription ID to use.
+ * @param executor The executor on which you wish to execute callbacks.
* @param callback A callback object on which you wish to receive results of asynchronous
* operations.
* @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
*/
public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
- @NonNull Executor executor, int subscriptionId,
+ int subscriptionId, @NonNull Executor executor,
final @NonNull MbmsGroupCallSessionCallback callback) {
if (!sIsInitialized.compareAndSet(false, true)) {
throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
@@ -138,11 +139,11 @@
/**
* Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
- * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
*/
public static MbmsGroupCallSession create(@NonNull Context context,
@NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
- return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+ return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
}
/**
@@ -153,7 +154,7 @@
* instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
* enqueued will still be delivered.
*
- * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+ * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
* obtain another instance of {@link MbmsGroupCallSession} immediately after this method
* returns.
*
@@ -189,18 +190,19 @@
* Asynchronous errors through the callback include any of the errors in
* {@link MbmsErrors.GeneralErrors}.
*
- * @param executor The executor on which you wish to execute callbacks for this stream.
* @param tmgi The TMGI, an identifier for the group call you want to join.
- * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+ * @param saiList A list of SAIs for the group call that should be negotiated separately with
* the carrier.
- * @param frequencyArray An array of frequencies for the group call that should be negotiated
+ * @param frequencyList A lost of frequencies for the group call that should be negotiated
* separately with the carrier.
+ * @param executor The executor on which you wish to execute callbacks for this stream.
* @param callback The callback that you want to receive information about the call on.
* @return An instance of {@link GroupCall} through which the call can be controlled.
* May be {@code null} if an error occurred.
*/
- public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
- int[] frequencyArray, @NonNull GroupCallCallback callback) {
+ public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+ @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+ @NonNull GroupCallCallback callback) {
IMbmsGroupCallService groupCallService = mService.get();
if (groupCallService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -215,7 +217,7 @@
try {
int returnCode = groupCallService.startGroupCall(
- mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+ mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
if (returnCode == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
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/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d2001ae..22ddb4a 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -16,11 +16,15 @@
package android.telephony;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
-import android.annotation.IntDef;
+import android.telephony.TelephonyManager.NetworkType;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
/**
* @hide
@@ -50,6 +54,7 @@
*
* <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
*/
+ @ConnectionStatus
private int mCellConnectionStatus;
/**
@@ -57,15 +62,33 @@
*/
private int mCellBandwidthDownlinkKhz;
- public PhysicalChannelConfig(int status, int bandwidth) {
- mCellConnectionStatus = status;
- mCellBandwidthDownlinkKhz = bandwidth;
- }
+ /**
+ * The radio technology for this physical channel.
+ */
+ @NetworkType
+ private int mRat;
- public PhysicalChannelConfig(Parcel in) {
- mCellConnectionStatus = in.readInt();
- mCellBandwidthDownlinkKhz = in.readInt();
- }
+ /**
+ * The rough frequency range for this physical channel.
+ */
+ @ServiceState.FrequencyRange
+ private int mFrequencyRange;
+
+ /**
+ * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown.
+ */
+ private int mChannelNumber;
+
+ /**
+ * A list of data calls mapped to this physical channel. An empty list means the physical
+ * channel has no data call mapped to it.
+ */
+ private int[] mContextIds;
+
+ /**
+ * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known.
+ */
+ private int mPhysicalCellId;
@Override
public int describeContents() {
@@ -76,6 +99,11 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mCellConnectionStatus);
dest.writeInt(mCellBandwidthDownlinkKhz);
+ dest.writeInt(mRat);
+ dest.writeInt(mChannelNumber);
+ dest.writeInt(mFrequencyRange);
+ dest.writeIntArray(mContextIds);
+ dest.writeInt(mPhysicalCellId);
}
/**
@@ -86,6 +114,60 @@
}
/**
+ * Get the list of data call ids mapped to this physical channel. This list is sorted into
+ * ascending numerical order. Each id in this list must match the id in
+ * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the
+ * physical channel has no data call mapped to it.
+ *
+ * @return an integer list indicates the data call ids.
+ */
+ public int[] getContextIds() {
+ return mContextIds;
+ }
+
+ /**
+ * @return the rough frequency range for this physical channel.
+ * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
+ * @see {@link ServiceState#FREQUENCY_RANGE_MID}
+ * @see {@link ServiceState#FREQUENCY_RANGE_HIGH}
+ * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE}
+ */
+ @ServiceState.FrequencyRange
+ public int getFrequencyRange() {
+ return mFrequencyRange;
+ }
+
+ /**
+ * @return the absolute radio frequency channel number for this physical channel,
+ * {@link Integer#MAX_VALUE} if unknown.
+ */
+ public int getChannelNumber() {
+ return mChannelNumber;
+ }
+
+ /**
+ * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+ * Reference: 3GPP TS 25.213 section 5.2.2.
+ *
+ * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+ * Reference: 3GPP TS 36.211 section 6.11.
+ *
+ * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008].
+ * Reference: 3GPP TS 38.211 section 7.4.2.1.
+ *
+ * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown.
+ */
+ public int getPhysicalCellId() {
+ return mPhysicalCellId;
+ }
+
+ /**The radio technology for this physical channel. */
+ @NetworkType
+ public int getRat() {
+ return mRat;
+ }
+
+ /**
* Gets the connection status of the cell.
*
* @see #CONNECTION_PRIMARY_SERVING
@@ -125,12 +207,19 @@
PhysicalChannelConfig config = (PhysicalChannelConfig) o;
return mCellConnectionStatus == config.mCellConnectionStatus
- && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+ && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz
+ && mRat == config.mRat
+ && mFrequencyRange == config.mFrequencyRange
+ && mChannelNumber == config.mChannelNumber
+ && mPhysicalCellId == config.mPhysicalCellId
+ && Arrays.equals(mContextIds, config.mContextIds);
}
@Override
public int hashCode() {
- return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+ return Objects.hash(
+ mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange,
+ mChannelNumber, mPhysicalCellId, mContextIds);
}
public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
@@ -147,11 +236,111 @@
@Override
public String toString() {
return new StringBuilder()
- .append("{mConnectionStatus=")
- .append(getConnectionStatusString())
- .append(",mCellBandwidthDownlinkKhz=")
- .append(mCellBandwidthDownlinkKhz)
- .append("}")
- .toString();
+ .append("{mConnectionStatus=")
+ .append(getConnectionStatusString())
+ .append(",mCellBandwidthDownlinkKhz=")
+ .append(mCellBandwidthDownlinkKhz)
+ .append(",mRat=")
+ .append(mRat)
+ .append(",mFrequencyRange=")
+ .append(mFrequencyRange)
+ .append(",mChannelNumber=")
+ .append(mChannelNumber)
+ .append(",mContextIds=")
+ .append(mContextIds.toString())
+ .append(",mPhysicalCellId=")
+ .append(mPhysicalCellId)
+ .append("}")
+ .toString();
+ }
+
+ private PhysicalChannelConfig(Parcel in) {
+ mCellConnectionStatus = in.readInt();
+ mCellBandwidthDownlinkKhz = in.readInt();
+ mRat = in.readInt();
+ mChannelNumber = in.readInt();
+ mFrequencyRange = in.readInt();
+ mContextIds = in.createIntArray();
+ mPhysicalCellId = in.readInt();
+ }
+
+ private PhysicalChannelConfig(Builder builder) {
+ mCellConnectionStatus = builder.mCellConnectionStatus;
+ mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz;
+ mRat = builder.mRat;
+ mChannelNumber = builder.mChannelNumber;
+ mFrequencyRange = builder.mFrequencyRange;
+ mContextIds = builder.mContextIds;
+ mPhysicalCellId = builder.mPhysicalCellId;
+ }
+
+ /** The builder of {@code PhysicalChannelConfig}. */
+ public static final class Builder {
+ private int mRat;
+ private int mFrequencyRange;
+ private int mChannelNumber;
+ private int mCellBandwidthDownlinkKhz;
+ private int mCellConnectionStatus;
+ private int[] mContextIds;
+ private int mPhysicalCellId;
+
+ /** @hide */
+ public Builder() {
+ mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+ mChannelNumber = Integer.MAX_VALUE;
+ mCellBandwidthDownlinkKhz = 0;
+ mCellConnectionStatus = CONNECTION_UNKNOWN;
+ mContextIds = new int[0];
+ mPhysicalCellId = Integer.MAX_VALUE;
+ }
+
+ /** @hide */
+ public PhysicalChannelConfig build() {
+ return new PhysicalChannelConfig(this);
+ }
+
+ /** @hide */
+ public Builder setRat(int rat) {
+ this.mRat = rat;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setFrequencyRange(int frequencyRange) {
+ this.mFrequencyRange = frequencyRange;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setChannelNumber(int channelNumber) {
+ this.mChannelNumber = channelNumber;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
+ this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setCellConnectionStatus(int connectionStatus) {
+ this.mCellConnectionStatus = connectionStatus;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setContextIds(int[] contextIds) {
+ if (contextIds != null) Arrays.sort(contextIds);
+ this.mContextIds = contextIds;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setPhysicalCellId(int physicalCellId) {
+ this.mPhysicalCellId = physicalCellId;
+ return this;
+ }
}
}
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index da3acc2..4482074 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -460,6 +460,9 @@
if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
}
+ if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) {
+ networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+ }
return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 777d219..ca0c854 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -118,6 +118,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_",
@@ -223,9 +230,15 @@
public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
/**
- * Number of radio technologies for GSM, UMTS and CDMA.
+ * NR(New Radio) 5G.
+ * @hide
*/
- private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20;
+ public static final int RIL_RADIO_TECHNOLOGY_NR = 20;
+
+ /**
+ * The number of the radio technologies.
+ */
+ private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21;
/** @hide */
public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
@@ -1829,4 +1842,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/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 3200aea..2c06c47 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -147,7 +147,8 @@
public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
/**
- * A content {@link Uri} used to receive updates on advanced calling user setting.
+ * A content {@link Uri} used to receive updates on advanced calling user setting
+ * @see ImsMmTelManager#isAdvancedCallingSettingEnabled().
* <p>
* Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
* subscription advanced calling enabled
@@ -406,6 +407,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
@@ -568,14 +576,6 @@
public static final String IS_OPPORTUNISTIC = "is_opportunistic";
/**
- * TelephonyProvider column name for subId of parent subscription of an opportunistic
- * subscription.
- * if the parent sub id is valid, then is_opportunistic should always to true.
- * @hide
- */
- public static final String PARENT_SUB_ID = "parent_sub_id";
-
- /**
* TelephonyProvider column name for group ID. Subscriptions with same group ID
* are considered bundled together, and should behave as a single subscription at
* certain scenarios.
@@ -583,7 +583,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>
@@ -1426,7 +1431,7 @@
* subscriptions in the slot.
*/
@Nullable
- public static int[] getSubscriptionIds(int slotIndex) {
+ public int[] getSubscriptionIds(int slotIndex) {
return getSubId(slotIndex);
}
@@ -1585,14 +1590,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
@@ -1640,14 +1654,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
@@ -2410,6 +2433,21 @@
return groupUUID;
}
+ /**
+ * 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));
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 79ed93e..fa9b76d 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;
@@ -1341,6 +1342,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
@@ -2243,9 +2251,11 @@
/** Current network is LTE_CA {@hide} */
@UnsupportedAppUsage
public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+ /** Current network is NR(New Radio) 5G. */
+ public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
/** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
- public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
+ public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_NR;
/** @hide */
@IntDef({
@@ -2269,6 +2279,7 @@
NETWORK_TYPE_TD_SCDMA,
NETWORK_TYPE_IWLAN,
NETWORK_TYPE_LTE_CA,
+ NETWORK_TYPE_NR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface NetworkType{}
@@ -4214,7 +4225,8 @@
}
/**
- * Returns the voice mail count for a subscription. Return 0 if unavailable.
+ * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does
+ * not have the READ_PHONE_STATE permission.
* @param subId whose voice message count is returned
* @hide
*/
@@ -4225,7 +4237,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return 0;
- return telephony.getVoiceMessageCountForSubscriber(subId);
+ return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName());
} catch (RemoteException ex) {
return 0;
} catch (NullPointerException ex) {
@@ -5368,7 +5380,7 @@
/**
* Rollback modem configurations to factory default except some config which are in whitelist.
- * Used for device configuration by some CDMA operators.
+ * Used for device configuration by some carriers.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5395,7 +5407,7 @@
}
/**
- * Generate a radio modem reset. Used for device configuration by some CDMA operators.
+ * Generate a radio modem reset. Used for device configuration by some carriers.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5499,6 +5511,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
@@ -8579,7 +8658,8 @@
/**
* Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
- * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
+ * All uicc applications are uniquely identified by application ID, represented by the hex
+ * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
*
@@ -9216,6 +9296,7 @@
NETWORK_TYPE_BITMASK_TD_SCDMA,
NETWORK_TYPE_BITMASK_LTE,
NETWORK_TYPE_BITMASK_LTE_CA,
+ NETWORK_TYPE_BITMASK_NR,
})
public @interface NetworkTypeBitMask {}
@@ -9332,6 +9413,13 @@
public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
/**
+ * network type bitmask indicating the support of radio tech NR(New Radio) 5G.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR);
+
+ /**
* @return Modem supported radio access family bitmask
*
* <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
@@ -9520,4 +9608,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..a1afc08 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];
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index bdba8c8..41f7bd7 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,8 +17,9 @@
package android.telephony.emergency;
import android.annotation.IntDef;
-import android.hardware.radio.V1_3.EmergencyNumberSource;
-import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.annotation.NonNull;
+import android.hardware.radio.V1_4.EmergencyNumberSource;
+import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
import android.os.Parcelable;
@@ -138,7 +139,7 @@
}
/**
- * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+ * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
*
* The emergency number has one or more defined emergency number sources.
*
@@ -196,7 +197,7 @@
private final int mEmergencyNumberSourceBitmask;
/** @hide */
- public EmergencyNumber(String number, String countryIso,
+ public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
int emergencyServiceCategories,
int emergencyNumberSources) {
this.mNumber = number;
@@ -403,7 +404,7 @@
* 0 if both have equal display priority.
*/
@Override
- public int compareTo(EmergencyNumber emergencyNumber) {
+ public int compareTo(@NonNull EmergencyNumber emergencyNumber) {
if (this.getDisplayPriorityScore()
> emergencyNumber.getDisplayPriorityScore()) {
return -1;
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/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index f73036e..a6c24bf 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -23,6 +23,8 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.VideoProfile;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
@@ -295,6 +297,28 @@
public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
/**
+ * The emergency service categories, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ private @EmergencyServiceCategories int mEmergencyServiceCategories =
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+
+ /**
* Extras associated with this {@link ImsCallProfile}.
* <p>
* Valid data types include:
@@ -495,6 +519,7 @@
out.writeInt(mCallType);
out.writeBundle(filteredExtras);
out.writeParcelable(mMediaProfile, 0);
+ out.writeInt(mEmergencyServiceCategories);
}
private void readFromParcel(Parcel in) {
@@ -502,6 +527,7 @@
mCallType = in.readInt();
mCallExtras = in.readBundle();
mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+ mEmergencyServiceCategories = in.readInt();
}
public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -710,4 +736,53 @@
private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
return (videoState & videoStateToCheck) == videoStateToCheck;
}
+
+ /**
+ * Set the emergency service categories. The set value is valid only if
+ * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ public void setEmergencyServiceCategories(
+ @EmergencyServiceCategories int emergencyServiceCategories) {
+ mEmergencyServiceCategories = emergencyServiceCategories;
+ }
+
+ /**
+ * Get the emergency service categories, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * @return the emergency service categories,
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ public @EmergencyServiceCategories int getEmergencyServiceCategories() {
+ return mEmergencyServiceCategories;
+ }
}
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
index 9aca18e..25e274e 100644
--- a/telephony/java/android/telephony/mbms/GroupCall.java
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.RemoteException;
import android.telephony.MbmsGroupCallSession;
import android.telephony.mbms.vendor.IMbmsGroupCallService;
@@ -24,6 +25,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* Class used to represent a single MBMS group call. After a call has been started with
@@ -41,8 +43,26 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
public @interface GroupCallState {}
+
+ /**
+ * Indicates that the group call is in a stopped state
+ *
+ * This can be reported after network action or after calling {@link #close}.
+ */
public static final int STATE_STOPPED = 1;
+
+ /**
+ * Indicates that the group call is started.
+ *
+ * Data can be transmitted and received in this state.
+ */
public static final int STATE_STARTED = 2;
+
+ /**
+ * Indicates that the group call is stalled.
+ *
+ * This may be due to a network issue or the device being temporarily out of range.
+ */
public static final int STATE_STALLED = 3;
/**
@@ -122,16 +142,17 @@
* Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
* information of the group call has * changed. Callers must obtain this information from the
* wireless carrier independently.
- * @param saiArray New array of SAIs that the call is available on.
- * @param frequencyArray New array of frequencies that the call is available on.
+ * @param saiList New list of SAIs that the call is available on.
+ * @param frequencyList New list of frequencies that the call is available on.
*/
- public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+ public void updateGroupCall(@NonNull List<Integer> saiList,
+ @NonNull List<Integer> frequencyList) {
if (mService == null) {
throw new IllegalStateException("No group call service attached");
}
try {
- mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+ mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 001bb02..77e36bb 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.Nullable;
import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
* A callback class for use when the application is in a group call. The middleware
* will provide updates on the status of the call via this callback.
*/
-public class GroupCallCallback {
+public interface GroupCallCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -40,7 +41,7 @@
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
- private @interface GroupCallError{}
+ @interface GroupCallError{}
/**
* Indicates broadcast signal strength is not available for this call.
@@ -48,7 +49,7 @@
* This may be due to the call no longer being available due to geography
* or timing (end of service)
*/
- public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+ int SIGNAL_STRENGTH_UNAVAILABLE = -1;
/**
* Called by the middleware when it has detected an error condition in this group call. The
@@ -56,9 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(@GroupCallError int errorCode, @Nullable String message) {
- // default implementation empty
- }
+ void onError(@GroupCallError int errorCode, @Nullable String message);
/**
* Called to indicate this call has changed state.
@@ -66,10 +65,8 @@
* See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
* and {@link GroupCall#STATE_STALLED}.
*/
- public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
- @GroupCall.GroupCallStateChangeReason int reason) {
- // default implementation empty
- }
+ void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+ @GroupCall.GroupCallStateChangeReason int reason);
/**
* Broadcast Signal Strength updated.
@@ -81,7 +78,5 @@
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this call due to timing, geography or popularity.
*/
- public void onBroadcastSignalStrengthUpdated(int signalStrength) {
- // default implementation empty
- }
+ void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
}
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 7c4321b..52e4d33 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -140,5 +140,21 @@
public static final int ERROR_UNKNOWN_FILE_INFO = 403;
}
+ /**
+ * Indicates the errors that are applicable only to the group call use-case.
+ */
+ public static class GroupCallErrors {
+ private GroupCallErrors() { }
+ /** Indicates that the middleware was unable to start the group call. */
+ public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+ /**
+ * Indicates that the app called
+ * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+ * same {@code tmgi}.
+ */
+ public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+ }
+
private MbmsErrors() {}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 7da734e..04e7ba1 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.telephony.MbmsGroupCallSession;
@@ -29,9 +30,9 @@
/**
* A callback class that is used to receive information from the middleware on MBMS group-call
* services. An instance of this object should be passed into
- * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
*/
-public class MbmsGroupCallSessionCallback {
+public interface MbmsGroupCallSessionCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -48,7 +49,7 @@
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
- private @interface GroupCallError{}
+ @interface GroupCallError{}
/**
* Called by the middleware when it has detected an error condition. The possible error codes
@@ -56,8 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(@GroupCallError int errorCode, @Nullable String message) {
- }
+ void onError(@GroupCallError int errorCode, @Nullable String message);
/**
* Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,21 +70,22 @@
* @param availableSais A list of lists of available SAIS in neighboring cells, where each list
* contains the available SAIs in an individual cell.
*/
- public void onAvailableSaisUpdated(List<Integer> currentSais,
- List<List<Integer>> availableSais) {
- }
+ void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+ @NonNull List<List<Integer>> availableSais);
/**
* Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
- * via this callback may be used to establish a data-link interface with the modem before the
- * middleware is ready.
- * Note that this method may be called before {@link #onMiddlewareReady()}.
+ * via this callback may be used to establish a data-link interface with the modem.
+ *
+ * In order to establish the data-link interface, the multicast IP and port must be obtained
+ * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+ * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+ * callback.
*
* @param interfaceName The interface name for the data link.
* @param index The index for the data link.
*/
- public void onServiceInterfaceAvailable(String interfaceName, int index) {
- }
+ void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
/**
* Called to indicate that the middleware has been initialized and is ready.
@@ -94,6 +95,5 @@
* delivered via {@link #onError(int, String)} with error code
* {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/
- public void onMiddlewareReady() {
- }
+ void onMiddlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 721256a..44cc24a 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,11 +29,11 @@
void stopGroupCall(int subId, long tmgi);
- void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
- in int[] frequencyArray);
+ void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
+ in List frequencyList);
- int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
- in int[] frequencyArray, IGroupCallCallback callback);
+ int startGroupCall(int subscriptionId, long tmgi, in List saiList,
+ in List frequencyList, IGroupCallCallback callback);
void dispose(int subId);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 3734ca7..e86a47d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -115,15 +115,16 @@
}
@Override
- public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
- int[] frequencyArray) {
+ public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+ List frequencyList) {
MbmsGroupCallServiceBase.this.updateGroupCall(
- subscriptionId, tmgi, saiArray, frequencyArray);
+ subscriptionId, tmgi, saiList, frequencyList);
}
@Override
- public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
- final int[] frequencyArray, final IGroupCallCallback callback)
+ public int startGroupCall(final int subscriptionId, final long tmgi,
+ final List saiList,
+ final List frequencyList, final IGroupCallCallback callback)
throws RemoteException {
if (callback == null) {
throw new NullPointerException("Callback must not be null");
@@ -132,7 +133,7 @@
final int uid = Binder.getCallingUid();
int result = MbmsGroupCallServiceBase.this.startGroupCall(
- subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+ subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
@Override
public void onError(final int errorCode, final String message) {
try {
@@ -209,13 +210,13 @@
*
* @param subscriptionId The subscription id to use.
* @param tmgi The TMGI, an identifier for the group call.
- * @param saiArray An array of SAIs for the group call.
- * @param frequencyArray An array of frequencies for the group call.
+ * @param saiList A list of SAIs for the group call.
+ * @param frequencyList A list of frequencies for the group call.
* @param callback The callback object on which the app wishes to receive updates.
* @return Any error in {@link MbmsErrors.GeneralErrors}
*/
- public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
- GroupCallCallback callback) {
+ public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+ List<Integer> frequencyList, GroupCallCallback callback) {
throw new UnsupportedOperationException("Not implemented");
}
@@ -237,11 +238,11 @@
/**
* Called when the app receives new SAI and frequency information for the group call identified
* by {@code tmgi}.
- * @param saiArray New array of SAIs that the call is available on.
- * @param frequencyArray New array of frequencies that the call is available on.
+ * @param saiList New list of SAIs that the call is available on.
+ * @param frequencyList New list of frequencies that the call is available on.
*/
- public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
- int[] frequencyArray) {
+ public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+ List<Integer> frequencyList) {
throw new UnsupportedOperationException("Not implemented");
}
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/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index bc44519..f9db4b0 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.
*
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index fc42de5..399dc52 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;
@@ -393,16 +395,11 @@
int getDataActivationState(int subId, String callingPackage);
/**
- * Returns the unread count of voicemails
- */
- int getVoiceMessageCount();
-
- /**
* Returns the unread count of voicemails for a subId.
* @param subId user preferred subId.
* Returns the unread count of voicemails
*/
- int getVoiceMessageCountForSubscriber(int subId);
+ int getVoiceMessageCountForSubscriber(int subId, String callingPackage);
/**
* Returns true if current state supports both voice and data
@@ -876,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
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 923ab06..76e7509 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -25,6 +25,7 @@
import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -80,4 +81,5 @@
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
void notifyPreferredDataSubIdChanged(int preferredSubId);
void notifyRadioPowerStateChanged(in int state);
+ void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
}
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index 7842a1c..5b58dd5 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -80,6 +80,7 @@
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
+ method public java.lang.String getOpPackageName();
method public java.lang.String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public java.lang.String getPackageName();
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index f1ec000..8b2c815 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -1,9 +1,5 @@
package android.test.mock {
- public class MockContext extends android.content.Context {
- method public java.lang.String getOpPackageName();
- }
-
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
method public boolean arePermissionsIndividuallyControlled();
method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 8d8fc84..b9e282e 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -38,7 +38,7 @@
public Engine onCreateEngine() {
return new Engine() {
@Override
- public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
ambientModeChangedCount[0]++;
}
};
@@ -47,12 +47,12 @@
WallpaperService.Engine engine = service.onCreateEngine();
engine.setCreated(true);
- engine.doAmbientModeChanged(false, false);
+ engine.doAmbientModeChanged(false, 0);
assertFalse("ambient mode should be false", engine.isInAmbientMode());
assertEquals("onAmbientModeChanged should have been called",
ambientModeChangedCount[0], 1);
- engine.doAmbientModeChanged(true, false);
+ engine.doAmbientModeChanged(true, 0);
assertTrue("ambient mode should be false", engine.isInAmbientMode());
assertEquals("onAmbientModeChanged should have been called",
ambientModeChangedCount[0], 2);
diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore
index bd79078..7f4121a 100644
--- a/tests/TouchLatency/.gitignore
+++ b/tests/TouchLatency/.gitignore
@@ -3,4 +3,5 @@
/.idea
.DS_Store
/build
+/gen
.iml
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 2337110..2594322 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion 28
+ buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.prefabulated.touchlatency"
minSdkVersion 21
- targetSdkVersion 21
+ targetSdkVersion 28
versionCode 1
versionName "1.0"
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index b4b5ca7..360c22f 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -19,11 +19,9 @@
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
-import android.os.CountDownTimer;
+import android.graphics.Paint.Align;
import android.os.Bundle;
-import android.text.method.Touch;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Menu;
@@ -31,15 +29,17 @@
import android.view.MotionEvent;
import android.view.View;
import android.os.Trace;
-
-import java.util.ArrayList;
-import java.util.Collections;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
class TouchLatencyView extends View implements View.OnTouchListener {
private static final String LOG_TAG = "TouchLatency";
private static final int BACKGROUND_COLOR = 0xFF400080;
private static final int INNER_RADIUS = 70;
- private static final int BALL_RADIUS = 100;
+ private static final int BALL_DIAMETER = 200;
+ private static final int SEC_TO_NANOS = 1000000000;
+ private static final float FPS_UPDATE_THRESHOLD = 20;
+ private static final long BALL_VELOCITY = 420;
public TouchLatencyView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -58,13 +58,17 @@
mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRedPaint.setColor(0xFFFF0000);
mRedPaint.setStyle(Paint.Style.FILL);
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setColor(0xFFFFFFFF);
+ mTextPaint.setTextSize(100);
+ mTextPaint.setTextAlign(Align.RIGHT);
mTouching = false;
- mBallX = 100.0f;
- mBallY = 100.0f;
- mVelocityX = 7.0f;
- mVelocityY = 7.0f;
+ mLastDrawNano = 0;
+ mFps = 0;
+ mLastFpsUpdate = 0;
+ mFrameCount = 0;
Trace.endSection();
}
@@ -113,43 +117,70 @@
}
}
+ private Paint getBallColor() {
+ if (mFps > 75)
+ return mGreenPaint;
+ else if (mFps > 45)
+ return mYellowPaint;
+ else
+ return mRedPaint;
+ }
+
private void drawBall(Canvas canvas) {
Trace.beginSection("TouchLatencyView drawBall");
int width = canvas.getWidth();
int height = canvas.getHeight();
+ float fps = 0f;
- // Update position
- mBallX += mVelocityX;
- mBallY += mVelocityY;
+ long t = System.nanoTime();
+ long tDiff = t - mLastDrawNano;
+ mLastDrawNano = t;
+ mFrameCount++;
- // Clamp and change velocity if necessary
- float left = mBallX - BALL_RADIUS;
- if (left < 0) {
- left = 0;
- mVelocityX *= -1;
+ if (tDiff < SEC_TO_NANOS) {
+ fps = 1f * SEC_TO_NANOS / tDiff;
}
- float top = mBallY - BALL_RADIUS;
- if (top < 0) {
- top = 0;
- mVelocityY *= -1;
+ long fDiff = t - mLastFpsUpdate;
+ if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+ mFps = fps;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ } else if (fDiff > SEC_TO_NANOS) {
+ mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
}
- float right = mBallX + BALL_RADIUS;
- if (right > width) {
- right = width;
- mVelocityX *= -1;
- }
+ final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+ final long xMax = width - BALL_DIAMETER;
+ final long yMax = height - BALL_DIAMETER;
+ long xOffset = pos % xMax;
+ long yOffset = pos % yMax;
- float bottom = mBallY + BALL_RADIUS;
- if (bottom > height) {
- bottom = height;
- mVelocityY *= -1;
+ float left, right, top, bottom;
+
+ if (((pos / xMax) & 1) == 0) {
+ left = xMax - xOffset;
+ } else {
+ left = xOffset;
}
+ right = left + BALL_DIAMETER;
+
+ if (((pos / yMax) & 1) == 0) {
+ top = yMax - yOffset;
+ } else {
+ top = yOffset;
+ }
+ bottom = top + BALL_DIAMETER;
// Draw the ball
canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawOval(left, top, right, bottom, mYellowPaint);
+ canvas.drawOval(left, top, right, bottom, getBallColor());
+ DecimalFormat df = new DecimalFormat("fps: #.##");
+ df.setRoundingMode(RoundingMode.HALF_UP);
+ canvas.drawText(df.format(mFps), width, 100, mTextPaint);
+
invalidate();
Trace.endSection();
}
@@ -176,15 +207,15 @@
Trace.endSection();
}
- private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint;
+ private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
private int mMode;
private boolean mTouching;
private float mTouchX, mTouchY;
private float mLastDrawnX, mLastDrawnY;
- private float mBallX, mBallY;
- private float mVelocityX, mVelocityY;
+ private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+ private float mFps;
}
public class TouchLatencyActivity extends Activity {
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index d3ff69d..03abe82 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -3,9 +3,10 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.1.0'
+ classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -15,5 +16,6 @@
allprojects {
repositories {
jcenter()
+ google()
}
}
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..111992a 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Nov 27 13:37:59 PST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index ae3914e..d5987a5 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -26,6 +26,7 @@
import android.view.Display;
import android.view.DisplayCutout;
import android.view.IWindowSession;
+import android.view.InsetsState;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
@@ -105,7 +106,7 @@
window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
- new Surface());
+ new Surface(), new InsetsState());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -131,8 +132,9 @@
final IWindowSession session = WindowManagerGlobal.getWindowSession();
final Rect tmpRect = new Rect();
try {
- final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams,
- View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect);
+ final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq,
+ layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect,
+ new InsetsState());
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 771faaf..be1a455 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -47,6 +47,7 @@
assertNull(c.getEncryption());
assertNull(c.getAuthentication());
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
+ assertEquals(0, c.getXfrmInterfaceId());
}
private IpSecConfig getSampleConfig() {
@@ -77,6 +78,7 @@
c.setNattKeepaliveInterval(42);
c.setMarkValue(12);
c.setMarkMask(23);
+ c.setXfrmInterfaceId(34);
return c;
}
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 9b919abf..d8f9618 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -71,6 +71,9 @@
private final LinkAddress mLocalInnerAddress;
private final int mFamily;
+ private static final int[] ADDRESS_FAMILIES =
+ new int[] {AF_INET, AF_INET6};
+
@Parameterized.Parameters
public static Collection ipSecConfigs() {
return Arrays.asList(
@@ -196,6 +199,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -231,6 +235,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -304,7 +309,8 @@
eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
eq(config.getEncapType()),
eq(encapSocketPort),
- eq(config.getEncapRemotePort()));
+ eq(config.getEncapRemotePort()),
+ eq(config.getXfrmInterfaceId()));
}
@Test
@@ -430,6 +436,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// quota is not released until the SPI is released by the Transform
assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -452,6 +459,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -469,6 +477,7 @@
anyString(),
anyInt(),
anyInt(),
+ anyInt(),
anyInt());
assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -504,6 +513,7 @@
anyString(),
eq(TEST_SPI),
anyInt(),
+ anyInt(),
anyInt());
// Verify quota and RefcountedResource objects cleaned up
@@ -532,7 +542,7 @@
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
+ eq(pfd),
eq(mUid),
eq(IpSecManager.DIRECTION_OUT),
anyString(),
@@ -545,7 +555,7 @@
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransforms(pfd);
- verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+ verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
}
private IpSecTunnelInterfaceResponse createAndValidateTunnel(
@@ -572,11 +582,12 @@
assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
verify(mMockNetd)
- .addVirtualTunnelInterface(
+ .ipSecAddTunnelInterface(
eq(createTunnelResp.interfaceName),
eq(mSourceAddr),
eq(mDestinationAddr),
anyInt(),
+ anyInt(),
anyInt());
}
@@ -591,7 +602,7 @@
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
- verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+ verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
try {
userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
createTunnelResp.resourceId);
@@ -614,7 +625,7 @@
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
- verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+ verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
try {
userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
createTunnelResp.resourceId);
@@ -624,6 +635,41 @@
}
@Test
+ public void testApplyTunnelModeTransform() throws Exception {
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ IpSecTunnelInterfaceResponse createTunnelResp =
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+ int transformResourceId = createTransformResp.resourceId;
+ int tunnelResourceId = createTunnelResp.resourceId;
+ mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+ transformResourceId, "blessedPackage");
+
+ for (int selAddrFamily : ADDRESS_FAMILIES) {
+ verify(mMockNetd)
+ .ipSecUpdateSecurityPolicy(
+ eq(mUid),
+ eq(selAddrFamily),
+ eq(IpSecManager.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI),
+ anyInt(), // iKey/oKey
+ anyInt(), // mask
+ eq(tunnelResourceId));
+ }
+
+ ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+ }
+
+ @Test
public void testAddRemoveAddressFromTunnelInterface() throws Exception {
for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
IpSecTunnelInterfaceResponse createTunnelResp =
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/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
index b399b0d..6e07b26 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
@@ -40,6 +40,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiManager;
import android.os.Handler;
+import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -70,6 +71,7 @@
private @Mock Handler mHandler;
private @Mock IpConnectivityLog mLogger;
private @Mock NetworkAgentInfo mAgent;
+ private @Mock NetworkAgentInfo mNotMeteredAgent;
private @Mock NetworkInfo mNetworkInfo;
private @Mock NetworkRequest mRequest;
private @Mock TelephonyManager mTelephony;
@@ -87,6 +89,10 @@
private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
+ private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+ private static final int RETURN_CODE_DNS_SUCCESS = 0;
+ private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
@Before
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
@@ -95,6 +101,12 @@
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
mAgent.networkInfo = mNetworkInfo;
+ mNotMeteredAgent.linkProperties = new LinkProperties();
+ mNotMeteredAgent.networkCapabilities = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ mNotMeteredAgent.networkInfo = mNetworkInfo;
+
when(mAgent.network()).thenReturn(mNetwork);
when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
when(mDependencies.getRandom()).thenReturn(mRandom);
@@ -138,6 +150,40 @@
when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
InetAddress.parseNumericAddress("192.168.0.0")
});
+
+ setMinDataStallEvaluateInterval(500);
+ setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS);
+ setValidDataStallDnsTimeThreshold(500);
+ setConsecutiveDnsTimeoutThreshold(5);
+ }
+
+ private class WrappedNetworkMonitor extends NetworkMonitor {
+ private long mProbeTime = 0;
+
+ WrappedNetworkMonitor(Context context, Handler handler,
+ NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+ IpConnectivityLog logger, Dependencies deps) {
+ super(context, handler, networkAgentInfo, defaultRequest, logger, deps);
+ }
+
+ @Override
+ protected long getLastProbeTime() {
+ return mProbeTime;
+ }
+
+ protected void setLastProbeTime(long time) {
+ mProbeTime = time;
+ }
+ }
+
+ WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
+ return new WrappedNetworkMonitor(
+ mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+ }
+
+ WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
+ return new WrappedNetworkMonitor(
+ mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies);
}
NetworkMonitor makeMonitor() {
@@ -272,6 +318,113 @@
assertPortal(makeMonitor().isCaptivePortal());
}
+ @Test
+ public void testIsDataStall_EvaluationDisabled() {
+ setDataStallEvaluationType(0);
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ assertFalse(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertTrue(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ assertFalse(wrappedMonitor.isDataStall());
+
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertTrue(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertFalse(wrappedMonitor.isDataStall());
+ // Reset consecutive timeout counts.
+ makeDnsSuccessEvent(wrappedMonitor, 1);
+ makeDnsTimeoutEvent(wrappedMonitor, 2);
+ assertFalse(wrappedMonitor.isDataStall());
+
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertTrue(wrappedMonitor.isDataStall());
+
+ // Set the value to larger than the default dns log size.
+ setConsecutiveDnsTimeoutThreshold(51);
+ wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 50);
+ assertFalse(wrappedMonitor.isDataStall());
+
+ makeDnsTimeoutEvent(wrappedMonitor, 1);
+ assertTrue(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
+ // Test dns events happened in valid dns time threshold.
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertFalse(wrappedMonitor.isDataStall());
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ assertTrue(wrappedMonitor.isDataStall());
+
+ // Test dns events happened before valid dns time threshold.
+ setValidDataStallDnsTimeThreshold(0);
+ wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertFalse(wrappedMonitor.isDataStall());
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ assertFalse(wrappedMonitor.isDataStall());
+ }
+
+ private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+ for (int i = 0; i < count; i++) {
+ wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+ RETURN_CODE_DNS_TIMEOUT);
+ }
+ }
+
+ private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+ for (int i = 0; i < count; i++) {
+ wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+ RETURN_CODE_DNS_SUCCESS);
+ }
+ }
+
+ private void setDataStallEvaluationType(int type) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
+ }
+
+ private void setMinDataStallEvaluateInterval(int time) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
+ }
+
+ private void setValidDataStallDnsTimeThreshold(int time) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
+ }
+
+ private void setConsecutiveDnsTimeoutThreshold(int num) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt()))
+ .thenReturn(num);
+ }
+
private void setFallbackUrl(String url) {
when(mDependencies.getSetting(any(),
eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index af7123b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -27,9 +27,17 @@
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
@@ -40,6 +48,8 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -48,12 +58,19 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PermissionMonitorTest {
- private static final int MOCK_UID = 10001;
- private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+ private static final int MOCK_USER1 = 0;
+ private static final int MOCK_USER2 = 1;
+ private static final int MOCK_UID1 = 10001;
+ private static final String MOCK_PACKAGE1 = "appName1";
+ private static final String SYSTEM_PACKAGE1 = "sysName1";
+ private static final String SYSTEM_PACKAGE2 = "sysName2";
private static final String PARTITION_SYSTEM = "system";
private static final String PARTITION_OEM = "oem";
private static final String PARTITION_PRODUCT = "product";
@@ -63,6 +80,7 @@
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
+ @Mock private INetworkManagementService mNMS;
private PermissionMonitor mPermissionMonitor;
@@ -70,8 +88,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
- mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
}
private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -80,7 +97,8 @@
packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
packageInfo.applicationInfo.uid = uid;
when(mPackageManager.getPackageInfoAsUser(
- eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+ eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
}
@@ -143,16 +161,16 @@
@Test
public void testHasUseBackgroundNetworksPermission() throws Exception {
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
}
@Test
@@ -172,15 +190,150 @@
@Test
public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
- assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
- assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+ }
+
+ private class NMSMonitor {
+ private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+ NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+ // Add hook to verify and track result of setPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final Boolean isSystem = args[0].equals("SYSTEM");
+ for (final int uid : (int[]) args[1]) {
+ // TODO: Currently, permission monitor will send duplicate commands for each uid
+ // corresponding to each user. Need to fix that and uncomment below test.
+ // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+ // fail("uid " + uid + " is already set to " + isSystem);
+ // }
+ mApps.put(uid, isSystem);
+ }
+ return null;
+ }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+ // Add hook to verify and track result of clearPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ for (final int uid : (int[]) args[0]) {
+ // TODO: Currently, permission monitor will send duplicate commands for each uid
+ // corresponding to each user. Need to fix that and uncomment below test.
+ // if (!mApps.containsKey(uid)) {
+ // fail("uid " + uid + " does not exist.");
+ // }
+ mApps.remove(uid);
+ }
+ return null;
+ }).when(mockNMS).clearPermission(any(int[].class));
+ }
+
+ public void expectPermission(Boolean permission, int[] users, int[] apps) {
+ for (final int user : users) {
+ for (final int app : apps) {
+ final int uid = UserHandle.getUid(user, app);
+ if (!mApps.containsKey(uid)) {
+ fail("uid " + uid + " does not exist.");
+ }
+ if (mApps.get(uid) != permission) {
+ fail("uid " + uid + " has wrong permission: " + permission);
+ }
+ }
+ }
+ }
+
+ public void expectNoPermission(int[] users, int[] apps) {
+ for (final int user : users) {
+ for (final int app : apps) {
+ final int uid = UserHandle.getUid(user, app);
+ if (mApps.containsKey(uid)) {
+ fail("uid " + uid + " has listed permissions, expected none.");
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testUserAndPackageAddRemove() throws Exception {
+ final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+ // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+ // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+ // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(SYSTEM_PACKAGE1));
+ doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(SYSTEM_PACKAGE2));
+ doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(MOCK_PACKAGE1));
+
+ // Add SYSTEM_PACKAGE2, expect only have network permission.
+ mPermissionMonitor.onUserAdded(MOCK_USER1);
+ addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+ // Add SYSTEM_PACKAGE1, expect permission escalate.
+ addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+ mPermissionMonitor.onUserAdded(MOCK_USER2);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{MOCK_UID1});
+
+ // Remove MOCK_UID1, expect no permission left for all user.
+ mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+ // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ mPermissionMonitor.onUserRemoved(MOCK_USER1);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+ // Remove all packages, expect no permission left.
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+ removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID, MOCK_UID1});
+
+ // Remove last user, expect no redundant clearPermission is invoked.
+ mPermissionMonitor.onUserRemoved(MOCK_USER2);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID, MOCK_UID1});
+ }
+
+ // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+ // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+ // called multiple times with the uid corresponding to each user.
+ private void addPackageForUsers(int[] users, String packageName, int uid) {
+ for (final int user : users) {
+ mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+ }
+ }
+
+ private void removePackageForUsers(int[] users, int uid) {
+ for (final int user : users) {
+ mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+ }
}
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a6ed9f2..8081812 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -23,15 +23,15 @@
import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
@@ -39,19 +39,18 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -91,9 +90,9 @@
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
-import android.os.test.TestLooper;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -126,7 +125,6 @@
public class TetheringTest {
private static final int IFINDEX_OFFSET = 100;
- private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
@@ -370,61 +368,6 @@
mServiceContext.unregisterReceiver(mBroadcastReceiver);
}
- private void setupForRequiredProvisioning() {
- // Produce some acceptable looking provision app setting if requested.
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(PROVISIONING_APP_NAME);
- // Don't disable tethering provisioning unless requested.
- when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
- anyBoolean())).thenReturn(false);
- // Act like the CarrierConfigManager is present and ready unless told otherwise.
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(mCarrierConfigManager);
- when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
- }
-
- @Test
- public void canRequireProvisioning() {
- setupForRequiredProvisioning();
- sendConfigurationChanged();
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void toleratesCarrierConfigManagerMissing() {
- setupForRequiredProvisioning();
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(null);
- sendConfigurationChanged();
- // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
- // We therefore still require provisioning.
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void toleratesCarrierConfigMissing() {
- setupForRequiredProvisioning();
- when(mCarrierConfigManager.getConfig()).thenReturn(null);
- sendConfigurationChanged();
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void provisioningNotRequiredWhenAppNotFound() {
- setupForRequiredProvisioning();
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(null);
- assertTrue(!mTethering.isTetherProvisioningRequired());
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(new String[] {"malformedApp"});
- assertTrue(!mTethering.isTetherProvisioningRequired());
- }
-
private void sendWifiApStateChanged(int state) {
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.putExtra(EXTRA_WIFI_AP_STATE, state);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
new file mode 100644
index 0000000..0f72229
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import com.android.internal.R;
+import com.android.server.connectivity.MockableSystemProperties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class EntitlementManagerTest {
+
+ private static final int EVENT_EM_UPDATE = 1;
+ private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+
+ @Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private Context mContext;
+ @Mock private ContentResolver mContent;
+ @Mock private MockableSystemProperties mSystemProperties;
+ @Mock private Resources mResources;
+ @Mock private SharedLog mLog;
+
+ // Like so many Android system APIs, these cannot be mocked because it is marked final.
+ // We have to use the real versions.
+ private final PersistableBundle mCarrierConfig = new PersistableBundle();
+
+ private EntitlementManager mEnMgr;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getContentResolver()).thenReturn(mContent);
+ when(mResources.getStringArray(R.array.config_tether_dhcp_range))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getIntArray(R.array.config_tether_upstream_types))
+ .thenReturn(new int[0]);
+ when(mLog.forSubComponent(anyString())).thenReturn(mLog);
+
+ mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ }
+
+ @After
+ public void tearDown() throws Exception {}
+
+ private void setupForRequiredProvisioning() {
+ // Produce some acceptable looking provision app setting if requested.
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(PROVISIONING_APP_NAME);
+ // Don't disable tethering provisioning unless requested.
+ when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
+ anyBoolean())).thenReturn(false);
+ // Act like the CarrierConfigManager is present and ready unless told otherwise.
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(mCarrierConfigManager);
+ when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+ }
+
+ @Test
+ public void canRequireProvisioning() {
+ setupForRequiredProvisioning();
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigManagerMissing() {
+ setupForRequiredProvisioning();
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+ // Therefore provisioning still be required.
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigMissing() {
+ setupForRequiredProvisioning();
+ when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ // We still have a provisioning app configured, so still require provisioning.
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void provisioningNotRequiredWhenAppNotFound() {
+ setupForRequiredProvisioning();
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertFalse(mEnMgr.isTetherProvisioningRequired());
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(new String[] {"malformedApp"});
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertFalse(mEnMgr.isTetherProvisioningRequired());
+ }
+
+}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index ed70fb3..df0daeb 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,72 @@
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());
+
+ std::vector<Overlayable::Policy> policies;
+ if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
+ policies.push_back(Overlayable::Policy::kPublic);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
+ policies.push_back(Overlayable::Policy::kSystem);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
+ policies.push_back(Overlayable::Policy::kVendor);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
+ policies.push_back(Overlayable::Policy::kProduct);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
+ policies.push_back(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;
+ }
+
+ for (Overlayable::Policy policy : policies) {
+ Overlayable overlayable;
+ overlayable.source = source_.WithLine(0);
+ overlayable.policy = policy;
+ if (!table_->AddOverlayable(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..976c328 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,97 @@
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>";
+
+ // TODO(b/120298168): Convert the policies vector to a policy set or bitmask
+ if (!entry->overlayable_declarations.empty()) {
+ uint16_t policy_flags = 0;
+ for (Overlayable overlayable : entry->overlayable_declarations) {
+ if (overlayable.policy) {
+ switch (overlayable.policy.value()) {
+ case Overlayable::Policy::kPublic:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ break;
+ case Overlayable::Policy::kSystem:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+ break;
+ case Overlayable::Policy::kVendor:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+ break;
+ case Overlayable::Policy::kProduct:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+ break;
+ case Overlayable::Policy::kProductServices:
+ policy_flags |=
+ ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+ break;
+ }
+ } else {
+ // 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 +545,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..410efbe 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,24 +628,108 @@
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
+ 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))
+ .AddOverlayable(name, Overlayable::Policy::kProduct)
+ .AddOverlayable(name, Overlayable::Policy::kSystem)
+ .AddOverlayable(name, Overlayable::Policy::kVendor)
.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));
-
- 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);
+ auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kSystem);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kVendor);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+ Overlayable::Policy::kProduct);
}
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
+ std::string name_zero = "com.app.test:integer/overlayable_zero";
+ std::string name_one = "com.app.test:integer/overlayable_one";
+ std::string name_two = "com.app.test:integer/overlayable_two";
+ std::string name_three = "com.app.test:integer/overlayable_three";
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple(name_zero, ResourceId(0x7f020000))
+ .AddOverlayable(name_zero, Overlayable::Policy::kProduct)
+ .AddOverlayable(name_zero, Overlayable::Policy::kSystem)
+ .AddOverlayable(name_zero, Overlayable::Policy::kProductServices)
+ .AddSimple(name_one, ResourceId(0x7f020001))
+ .AddOverlayable(name_one, Overlayable::Policy::kPublic)
+ .AddOverlayable(name_one, Overlayable::Policy::kSystem)
+ .AddSimple(name_two, ResourceId(0x7f020002))
+ .AddOverlayable(name_two, Overlayable::Policy::kProduct)
+ .AddOverlayable(name_two, Overlayable::Policy::kSystem)
+ .AddOverlayable(name_two, Overlayable::Policy::kProductServices)
+ .AddSimple(name_three, ResourceId(0x7f020003))
+ .AddOverlayable(name_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());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kSystem);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kProduct);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+ Overlayable::Policy::kProductServices);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 2);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kPublic);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kSystem);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kSystem);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kProduct);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+ Overlayable::Policy::kProductServices);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 1);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kPublic);
+
+}
+
+
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index d1a70a7..31d205e 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -298,19 +298,20 @@
"<colgroup align=\"left\" />\n"
"<tr><th>Attribute</th><th>Description</th></tr>\n";
- // Build the table of attributes with their links and names.
- for (const StyleableAttr& entry : sorted_attributes) {
- if (SkipSymbol(entry.symbol)) {
- continue;
- }
-
+ // Removed and hidden attributes are public but hidden from the documentation, so don't emit
+ // them as part of the class documentation.
+ std::vector<StyleableAttr> documentation_attrs = sorted_attributes;
+ auto documentation_remove_iter = std::remove_if(documentation_attrs.begin(),
+ documentation_attrs.end(),
+ [&](StyleableAttr entry) -> bool {
StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
- if (attr_comment_line.contains("@removed")) {
- // Removed attributes are public but hidden from the documentation, so
- // don't emit them as part of the class documentation.
- continue;
- }
+ return SkipSymbol(entry.symbol) || attr_comment_line.contains("@removed")
+ || attr_comment_line.contains("@hide");
+ });
+ documentation_attrs.erase(documentation_remove_iter, documentation_attrs.end());
+ // Build the table of attributes with their links and names.
+ for (const StyleableAttr& entry : documentation_attrs) {
const ResourceName& attr_name = entry.attr_ref->name.value();
styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
<< (!attr_name.package.empty() ? attr_name.package
@@ -320,16 +321,14 @@
// Only use the comment up until the first '.'. This is to stay compatible with
// the way old AAPT did it (presumably to keep it short and to avoid including
// annotations like @hide which would affect this Styleable).
+ StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
<< "</td></tr>\n";
}
styleable_comment << "</table>\n";
// Generate the @see lines for each attribute.
- for (const StyleableAttr& entry : sorted_attributes) {
- if (SkipSymbol(entry.symbol)) {
- continue;
- }
+ for (const StyleableAttr& entry : documentation_attrs) {
styleable_comment << "@see #" << entry.field_name << "\n";
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index fa208be..4f51fc4 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -366,6 +366,46 @@
ASSERT_TRUE(generator.Generate("android", &out));
out.Flush();
+ EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
+ EXPECT_THAT(output, HasSubstr("@see #Container_one"));
+ EXPECT_THAT(output, HasSubstr("attr name android:one"));
+ EXPECT_THAT(output, HasSubstr("attr description"));
+ EXPECT_THAT(output, HasSubstr(attr.GetComment()));
+ EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
+}
+
+TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
+ Attribute attr;
+ attr.SetComment(StringPiece("This is an attribute @hide"));
+
+ Styleable styleable;
+ styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
+ styleable.SetComment(StringPiece("This is a styleable"));
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
+ .AddValue("android:styleable/Container",
+ std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGeneratorOptions options;
+ options.use_final = false;
+ JavaClassGenerator generator(context.get(), table.get(), options);
+
+ std::string output;
+ StringOutputStream out(&output);
+ ASSERT_TRUE(generator.Generate("android", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
+ EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
EXPECT_THAT(output, HasSubstr("attr name android:one"));
EXPECT_THAT(output, HasSubstr("attr description"));
EXPECT_THAT(output, HasSubstr(attr.GetComment()));
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 3a5d585..1b6626a 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_declarations.empty() && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_declarations[0].source)
+ << "no definition for overlayable symbol '"
+ << name << "'");
error = true;
}
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index e50c70d..f25fcdc 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -19,6 +19,6 @@
echo "If your change contains no confidential details (such as security fixes), please"
echo "upload and merge this change at https://android-review.googlesource.com/."
echo
- exit 77
+ exit 1
fi
fi
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 91cd1cb..cb8fef9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1285,10 +1285,19 @@
if clazz.fullname == "android.os.UserManager": return
for m in clazz.methods:
- if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue
if re.match("on[A-Z]+", m.name): continue
- if "android.os.UserHandle" in m.args:
- warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'")
+
+ has_arg = "android.os.UserHandle" in m.args
+ has_name = m.name.endswith("AsUser") or m.name.endswith("ForUser")
+
+ if clazz.fullname.endswith("Manager") and has_arg:
+ warn(clazz, m, None, "When a method overload is needed to target a specific "
+ "UserHandle, callers should be directed to use "
+ "Context.createPackageContextAsUser() and re-obtain the relevant "
+ "Manager, and no new API should be added")
+ elif has_arg and not has_name:
+ warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' "
+ "or 'queryFooForUser'")
def verify_params(clazz):
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
new file mode 100755
index 0000000..2291e5a
--- /dev/null
+++ b/tools/hiddenapi/exclude.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -e
+# Make sure that entries are not added for packages that are already fully handled using
+# annotations.
+LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
+# Each team should add a <team>_PACKAGES and <team>_EMAIL with the list of packages and
+# the team email to use in the event of this detecting an entry in a <team> package. Also
+# add <team> to the TEAMS list.
+LIBCORE_PACKAGES="\
+ android.icu \
+ android.system \
+ com.android.bouncycastle \
+ com.android.conscrypt \
+ com.android.okhttp \
+ com.sun \
+ dalvik \
+ java \
+ javax \
+ libcore \
+ org.apache.harmony \
+ org.json \
+ org.w3c.dom \
+ org.xml.sax \
+ sun \
+ "
+LIBCORE_EMAIL=libcore-team@android.com
+
+# List of teams.
+TEAMS=LIBCORE
+
+# Generate the list of packages and convert to a regular expression.
+PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
+RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+ ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file))
+ if [[ -n "${ENTRIES}" ]]; then
+ echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+ echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
+ echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
+ # Partition the entries by team and provide contact details to aid in fixing the issue.
+ for t in ${TEAMS}
+ do
+ PACKAGES=$(eval echo \${${t}_PACKAGES})
+ RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+ TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+ if [[ -n "${TEAM_ENTRIES}" ]]; then
+ EMAIL=$(eval echo \${${t}_EMAIL})
+ echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
+ for i in ${ENTRIES}
+ do
+ echo -e "\e[33m ${i}\e[0m"
+ done
+ fi
+ done
+ exit 1
+ fi
+done
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index fdc800b..01728fa1 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -15,23 +15,56 @@
# limitations under the License.
"""
Generate API lists for non-SDK API enforcement.
-
-usage: generate-hiddenapi-lists.py [-h]
- --input-public INPUT_PUBLIC
- --input-private INPUT_PRIVATE
- [--input-whitelists [INPUT_WHITELISTS [INPUT_WHITELISTS ...]]]
- [--input-greylists [INPUT_GREYLISTS [INPUT_GREYLISTS ...]]]
- [--input-blacklists [INPUT_BLACKLISTS [INPUT_BLACKLISTS ...]]]
- --output-whitelist OUTPUT_WHITELIST
- --output-light-greylist OUTPUT_LIGHT_GREYLIST
- --output-dark-greylist OUTPUT_DARK_GREYLIST
- --output-blacklist OUTPUT_BLACKLIST
"""
import argparse
import os
import sys
import re
+# Names of flags recognized by the `hiddenapi` tool.
+FLAG_WHITELIST = "whitelist"
+FLAG_GREYLIST = "greylist"
+FLAG_BLACKLIST = "blacklist"
+FLAG_GREYLIST_MAX_O = "greylist-max-o"
+FLAG_GREYLIST_MAX_P = "greylist-max-p"
+
+# List of all known flags.
+FLAGS = [
+ FLAG_WHITELIST,
+ FLAG_GREYLIST,
+ FLAG_BLACKLIST,
+ FLAG_GREYLIST_MAX_O,
+ FLAG_GREYLIST_MAX_P,
+]
+FLAGS_SET = set(FLAGS)
+
+# Suffix used in command line args to express that only known and
+# otherwise unassigned entries should be assign the given flag.
+# For example, the P dark greylist is checked in as it was in P,
+# but signatures have changes since then. The flag instructs this
+# script to skip any entries which do not exist any more.
+FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts"
+
+# Regex patterns of fields/methods used in serialization. These are
+# considered public API despite being hidden.
+SERIALIZATION_PATTERNS = [
+ r'readObject\(Ljava/io/ObjectInputStream;\)V',
+ r'readObjectNoData\(\)V',
+ r'readResolve\(\)Ljava/lang/Object;',
+ r'serialVersionUID:J',
+ r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
+ r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
+ r'writeReplace\(\)Ljava/lang/Object;',
+]
+
+# Single regex used to match serialization API. It combines all the
+# SERIALIZATION_PATTERNS into a single regular expression.
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+
+# Predicates to be used with filter_apis.
+IS_UNASSIGNED = lambda api, flags: not flags
+IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+
def get_args():
"""Parses command line arguments.
@@ -39,21 +72,21 @@
Namespace: dictionary of parsed arguments
"""
parser = argparse.ArgumentParser()
- parser.add_argument('--input-public', required=True, help='List of all public members')
- parser.add_argument('--input-private', required=True, help='List of all private members')
- parser.add_argument(
- '--input-whitelists', nargs='*',
- help='Lists of members to force on whitelist')
- parser.add_argument(
- '--input-greylists', nargs='*',
- help='Lists of members to force on light greylist')
- parser.add_argument(
- '--input-blacklists', nargs='*',
- help='Lists of members to force on blacklist')
- parser.add_argument('--output-whitelist', required=True)
- parser.add_argument('--output-light-greylist', required=True)
- parser.add_argument('--output-dark-greylist', required=True)
- parser.add_argument('--output-blacklist', required=True)
+ parser.add_argument('--output', required=True)
+ parser.add_argument('--public', required=True, help='list of all public entries')
+ parser.add_argument('--private', required=True, help='list of all private entries')
+ parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+ help='CSV files to be merged into output')
+
+ for flag in FLAGS:
+ ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
+ parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
+ help='lists of entries with flag "' + flag + '"')
+ parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*',
+ default=[], metavar='TXT_FILE',
+ help='lists of entries with flag "' + flag +
+ '". skip entry if missing or flag conflict.')
+
return parser.parse_args()
def read_lines(filename):
@@ -65,10 +98,13 @@
filename (string): Path to the file to read from.
Returns:
- list: Lines of the loaded file as a list of strings.
+ Lines of the file as a list of string.
"""
with open(filename, 'r') as f:
- return filter(lambda line: not line.startswith('#'), f.readlines())
+ lines = f.readlines();
+ lines = filter(lambda line: not line.startswith('#'), lines)
+ lines = map(lambda line: line.strip(), lines)
+ return set(lines)
def write_lines(filename, lines):
"""Writes list of lines into a file, overwriting the file it it exists.
@@ -77,167 +113,168 @@
filename (string): Path to the file to be writting into.
lines (list): List of strings to write into the file.
"""
+ lines = map(lambda line: line + '\n', lines)
with open(filename, 'w') as f:
f.writelines(lines)
-def move_between_sets(subset, src, dst, source = "<unknown>"):
- """Removes a subset of elements from one set and add it to another.
+class FlagsDict:
+ def __init__(self, public_api, private_api):
+ # Bootstrap the entries dictionary.
- Args:
- subset (set): The subset of `src` to be moved from `src` to `dst`.
- src (set): Source set. Must be a superset of `subset`.
- dst (set): Destination set. Must be disjoint with `subset`.
- """
- assert src.issuperset(subset), (
- "Error processing: {}\n"
- "The following entries were not found:\n"
- "{}"
- "Please visit go/hiddenapi for more information.").format(
- source, "".join(map(lambda x: " " + str(x), subset.difference(src))))
- assert dst.isdisjoint(subset)
- # Order matters if `src` and `subset` are the same object.
- dst.update(subset)
- src.difference_update(subset)
+ # Check that the two sets do not overlap.
+ public_api_set = set(public_api)
+ private_api_set = set(private_api)
+ assert public_api_set.isdisjoint(private_api_set), (
+ "Lists of public and private API overlap. " +
+ "This suggests an issue with the `hiddenapi` build tool.")
-def get_package_name(signature):
- """Returns the package name prefix of a class member signature.
+ # Compute the whole key set
+ self._dict_keyset = public_api_set.union(private_api_set)
- Example: "Ljava/lang/String;->hashCode()J" --> "Ljava/lang/"
+ # Create a dict that creates entries for both public and private API,
+ # and assigns public API to the whitelist.
+ self._dict = {}
+ for api in public_api:
+ self._dict[api] = set([ FLAG_WHITELIST ])
+ for api in private_api:
+ self._dict[api] = set()
- Args:
- signature (string): Member signature
+ def _check_entries_set(self, keys_subset, source):
+ assert isinstance(keys_subset, set)
+ assert keys_subset.issubset(self._dict_keyset), (
+ "Error processing: {}\n"
+ "The following entries were unexpected:\n"
+ "{}"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "".join(map(lambda x: " " + str(x), keys_subset - self._dict_keyset)))
- Returns
- string: Package name of the given member
- """
- class_name_end = signature.find("->")
- assert class_name_end != -1, "Invalid signature: {}".format(signature)
- package_name_end = signature.rfind("/", 0, class_name_end)
- assert package_name_end != -1, "Invalid signature: {}".format(signature)
- return signature[:package_name_end + 1]
+ def _check_flags_set(self, flags_subset, source):
+ assert isinstance(flags_subset, set)
+ assert flags_subset.issubset(FLAGS_SET), (
+ "Error processing: {}\n"
+ "The following flags were not recognized: \n"
+ "{}\n"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "\n".join(flags_subset - FLAGS_SET))
-def all_package_names(*args):
- """Returns a set of packages names in given lists of member signatures.
+ def filter_apis(self, filter_fn):
+ """Returns APIs which match a given predicate.
- Example: args = [ set([ "Lpkg1/ClassA;->foo()V", "Lpkg2/ClassB;->bar()J" ]),
- set([ "Lpkg1/ClassC;->baz()Z" ]) ]
- return value = set([ "Lpkg1/", "Lpkg2" ])
+ This is a helper function which allows to filter on both signatures (keys) and
+ flags (values). The built-in filter() invokes the lambda only with dict's keys.
- Args:
- *args (list): List of sets to iterate over and extract the package names
- of its elements (member signatures)
+ Args:
+ filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
- Returns:
- set: All package names extracted from the given lists of signatures.
- """
- packages = set()
- for arg in args:
- packages = packages.union(map(get_package_name, arg))
- return packages
+ Returns:
+ A set of APIs which match the predicate.
+ """
+ return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
-def move_all(src, dst):
- """Moves all elements of one set to another.
+ def get_valid_subset_of_unassigned_apis(self, api_subset):
+ """Sanitizes a key set input to only include keys which exist in the dictionary
+ and have not been assigned any flags.
- Args:
- src (set): Source set. Will become empty.
- dst (set): Destination set. Will contain all elements of `src`.
- """
- move_between_sets(src, src, dst)
+ Args:
+ entries_subset (set/list): Key set to be sanitized.
-def move_from_files(filenames, src, dst):
- """Loads member signatures from a list of files and moves them to a given set.
+ Returns:
+ Sanitized key set.
+ """
+ assert isinstance(api_subset, set)
+ return api_subset.intersection(self.filter_apis(IS_UNASSIGNED))
- Opens files in `filenames`, reads all their lines and moves those from `src`
- set to `dst` set.
+ def generate_csv(self):
+ """Constructs CSV entries from a dictionary.
- Args:
- filenames (list): List of paths to files to be loaded.
- src (set): Set that loaded lines should be moved from.
- dst (set): Set that loaded lines should be moved to.
- """
- if filenames:
- for filename in filenames:
- move_between_sets(set(read_lines(filename)), src, dst, filename)
+ Returns:
+ List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+ """
+ return sorted(map(lambda api: ",".join([api] + sorted(self._dict[api])), self._dict))
-def move_serialization(src, dst):
- """Moves all members matching serialization API signatures between given sets.
+ def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+ """Parses CSV entries and merges them into a given dictionary.
- Args:
- src (set): Set that will be searched for serialization API and that API
- will be removed from it.
- dst (set): Set that serialization API will be moved to.
- """
- serialization_patterns = [
- r'readObject\(Ljava/io/ObjectInputStream;\)V',
- r'readObjectNoData\(\)V',
- r'readResolve\(\)Ljava/lang/Object;',
- r'serialVersionUID:J',
- r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
- r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
- r'writeReplace\(\)Ljava/lang/Object;',
- ]
- regex = re.compile(r'.*->(' + '|'.join(serialization_patterns) + r')$')
- move_between_sets(filter(lambda api: regex.match(api), src), src, dst)
+ The expected CSV format is:
+ <api signature>,<flag1>,<flag2>,...,<flagN>
-def move_from_packages(packages, src, dst):
- """Moves all members of given package names from one set to another.
+ Args:
+ csv_lines (list of strings): Lines read from a CSV file.
+ source (string): Origin of `csv_lines`. Will be printed in error messages.
- Args:
- packages (list): List of string package names.
- src (set): Set that will be searched for API matching one of the given
- package names. Surch API will be removed from the set.
- dst (set): Set that matching API will be moved to.
- """
- move_between_sets(filter(lambda api: get_package_name(api) in packages, src), src, dst)
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Split CSV lines into arrays of values.
+ csv_values = [ line.split(',') for line in csv_lines ]
+
+ # Check that all entries exist in the dict.
+ csv_keys = set([ csv[0] for csv in csv_values ])
+ self._check_entries_set(csv_keys, source)
+
+ # Check that all flags are known.
+ csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], []))
+ self._check_flags_set(csv_flags, source)
+
+ # Iterate over all CSV lines, find entry in dict and append flags to it.
+ for csv in csv_values:
+ self._dict[csv[0]].update(csv[1:])
+
+ def assign_flag(self, flag, apis, source="<unknown>"):
+ """Assigns a flag to given subset of entries.
+
+ Args:
+ flag (string): One of FLAGS.
+ apis (set): Subset of APIs to recieve the flag.
+ source (string): Origin of `entries_subset`. Will be printed in error messages.
+
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Check that all APIs exist in the dict.
+ self._check_entries_set(apis, source)
+
+ # Check that the flag is known.
+ self._check_flags_set(set([ flag ]), source)
+
+ # Iterate over the API subset, find each entry in dict and assign the flag to it.
+ for api in apis:
+ self._dict[api].add(flag)
def main(argv):
- args = get_args()
+ # Parse arguments.
+ args = vars(get_args())
- # Initialize API sets by loading lists of public and private API. Public API
- # are all members resolvable from SDK API stubs, other members are private.
- # As an optimization, skip the step of moving public API from a full set of
- # members and start with a populated whitelist.
- whitelist = set(read_lines(args.input_public))
- uncategorized = set(read_lines(args.input_private))
- light_greylist = set()
- dark_greylist = set()
- blacklist = set()
+ flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"]))
- # Assert that there is no overlap between public and private API.
- assert whitelist.isdisjoint(uncategorized)
- num_all_api = len(whitelist) + len(uncategorized)
+ # Combine inputs which do not require any particular order.
+ # (1) Assign serialization API to whitelist.
+ flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION))
- # Read all files which manually assign members to specific lists.
- move_from_files(args.input_whitelists, uncategorized, whitelist)
- move_from_files(args.input_greylists, uncategorized, light_greylist)
- move_from_files(args.input_blacklists, uncategorized, blacklist)
+ # (2) Merge input CSV files into the dictionary.
+ for filename in args["csv"]:
+ flags.parse_and_merge_csv(read_lines(filename), filename)
- # Iterate over all uncategorized members and move serialization API to whitelist.
- move_serialization(uncategorized, whitelist)
+ # (3) Merge text files with a known flag into the dictionary.
+ for flag in FLAGS:
+ for filename in args[flag]:
+ flags.assign_flag(flag, read_lines(filename), filename)
- # Extract package names of members from whitelist and light greylist, which
- # are assumed to have been finalized at this point. Assign all uncategorized
- # members from the same packages to the dark greylist.
- dark_greylist_packages = all_package_names(whitelist, light_greylist)
- move_from_packages(dark_greylist_packages, uncategorized, dark_greylist)
+ # Merge text files where conflicts should be ignored.
+ # This will only assign the given flag if:
+ # (a) the entry exists, and
+ # (b) it has not been assigned any other flag.
+ # Because of (b), this must run after all strict assignments have been performed.
+ for flag in FLAGS:
+ for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
+ valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
+ flags.assign_flag(flag, valid_entries, filename)
- # Assign all uncategorized members to the blacklist.
- move_all(uncategorized, blacklist)
+ # Assign all remaining entries to the blacklist.
+ flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED))
- # Assert we have not missed anything.
- assert whitelist.isdisjoint(light_greylist)
- assert whitelist.isdisjoint(dark_greylist)
- assert whitelist.isdisjoint(blacklist)
- assert light_greylist.isdisjoint(dark_greylist)
- assert light_greylist.isdisjoint(blacklist)
- assert dark_greylist.isdisjoint(blacklist)
- assert num_all_api == len(whitelist) + len(light_greylist) + len(dark_greylist) + len(blacklist)
-
- # Write final lists to disk.
- write_lines(args.output_whitelist, whitelist)
- write_lines(args.output_light_greylist, light_greylist)
- write_lines(args.output_dark_greylist, dark_greylist)
- write_lines(args.output_blacklist, blacklist)
+ # Write output.
+ write_lines(args["output"], flags.generate_csv())
if __name__ == "__main__":
main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 4716241..249f37d 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -2,14 +2,14 @@
#
# Copyright (C) 2018 The Android Open Source Project
#
-# Licensed under the Apache License, Version 2.0 (the "License");
+# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
+# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
@@ -18,90 +18,90 @@
from generate_hiddenapi_lists import *
class TestHiddenapiListGeneration(unittest.TestCase):
+ def test_init(self):
+ # Check empty lists
+ flags = FlagsDict([], [])
+ self.assertEquals(flags.generate_csv(), [])
- def test_move_between_sets(self):
- A = set([1, 2, 3, 4])
- B = set([5, 6, 7, 8])
- move_between_sets(set([2, 4]), A, B)
- self.assertEqual(A, set([1, 3]))
- self.assertEqual(B, set([2, 4, 5, 6, 7, 8]))
+ # Check valid input - two public and two private API signatures.
+ flags = FlagsDict(['A', 'B'], ['C', 'D'])
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_WHITELIST, 'B,' + FLAG_WHITELIST, 'C', 'D' ])
- def test_move_between_sets_fail_not_superset(self):
- A = set([1, 2, 3, 4])
- B = set([5, 6, 7, 8])
- with self.assertRaises(AssertionError) as ar:
- move_between_sets(set([0, 2]), A, B)
+ # Check invalid input - overlapping public/private API signatures.
+ with self.assertRaises(AssertionError):
+ flags = FlagsDict(['A', 'B'], ['B', 'C', 'D'])
- def test_move_between_sets_fail_not_disjoint(self):
- A = set([1, 2, 3, 4])
- B = set([4, 5, 6, 7, 8])
- with self.assertRaises(AssertionError) as ar:
- move_between_sets(set([1, 4]), A, B)
+ def test_filter_apis(self):
+ # Initialize flags so that A and B are put on the whitelist and
+ # C, D, E are left unassigned. Try filtering for the unassigned ones.
+ flags = FlagsDict(['A', 'B'], ['C', 'D', 'E'])
+ filter_set = flags.filter_apis(lambda api, flags: not flags)
+ self.assertTrue(isinstance(filter_set, set))
+ self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
- def test_get_package_name(self):
- self.assertEqual(get_package_name("Ljava/lang/String;->clone()V"), "Ljava/lang/")
+ def test_get_valid_subset_of_unassigned_keys(self):
+ # Create flags where only A is unassigned.
+ flags = FlagsDict(['A'], ['B', 'C'])
+ flags.assign_flag(FLAG_GREYLIST, set(['C']))
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_WHITELIST, 'B', 'C,' + FLAG_GREYLIST ])
- def test_get_package_name_fail_no_arrow(self):
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;-clone()V")
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;>clone()V")
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;__clone()V")
-
- def test_get_package_name_fail_no_package(self):
- with self.assertRaises(AssertionError) as ar:
- get_package_name("LString;->clone()V")
-
- def test_all_package_names(self):
- self.assertEqual(all_package_names(), set())
- self.assertEqual(all_package_names(set(["Lfoo/Bar;->baz()V"])), set(["Lfoo/"]))
+ # Check three things:
+ # (1) B is selected as valid unassigned
+ # (2) A is not selected because it is assigned 'whitelist'
+ # (3) D is not selected because it is not a valid key
self.assertEqual(
- all_package_names(set(["Lfoo/Bar;->baz()V", "Lfoo/BarX;->bazx()I"])),
- set(["Lfoo/"]))
- self.assertEqual(
- all_package_names(
- set(["Lfoo/Bar;->baz()V"]),
- set(["Lfoo/BarX;->bazx()I", "Labc/xyz/Mno;->ijk()J"])),
- set(["Lfoo/", "Labc/xyz/"]))
+ flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
- def test_move_all(self):
- src = set([ "abc", "xyz" ])
- dst = set([ "def" ])
- move_all(src, dst)
- self.assertEqual(src, set())
- self.assertEqual(dst, set([ "abc", "def", "xyz" ]))
+ def test_parse_and_merge_csv(self):
+ flags = FlagsDict(['A'], ['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
- def test_move_from_packages(self):
- src = set([ "Lfoo/bar/ClassA;->abc()J", # will be moved
- "Lfoo/bar/ClassA;->def()J", # will be moved
- "Lcom/pkg/example/ClassD;->ijk:J", # not moved: different package
- "Lfoo/bar/xyz/ClassC;->xyz()Z" ]) # not moved: subpackage
- dst = set()
- packages = set([ "Lfoo/bar/" ])
- move_from_packages(packages, src, dst)
- self.assertEqual(
- src, set([ "Lfoo/bar/xyz/ClassC;->xyz()Z", "Lcom/pkg/example/ClassD;->ijk:J" ]))
- self.assertEqual(
- dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ]))
+ # Test empty CSV entry.
+ flags.parse_and_merge_csv(['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
- def test_move_serialization(self):
- # All the entries should be moved apart from the last one
- src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V",
- "Lfoo/bar/ClassA;->readObjectNoData()V",
- "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;",
- "Lfoo/bar/ClassA;->serialVersionUID:J",
- "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;",
- "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V",
- "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;",
- # Should not be moved as signature does not match
- "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"])
- expectedToMove = len(src) - 1
- dst = set()
- packages = set([ "Lfoo/bar/" ])
- move_serialization(src, dst)
- self.assertEqual(len(src), 1)
- self.assertEqual(len(dst), expectedToMove)
+ # Test assigning an already assigned flag.
+ flags.parse_and_merge_csv(['A,' + FLAG_WHITELIST])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test new additions.
+ flags.parse_and_merge_csv([
+ 'A,' + FLAG_GREYLIST,
+ 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ])
+ self.assertEqual(flags.generate_csv(),
+ [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST,
+ 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ])
+
+ # Test unknown API signature.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'C' ])
+
+ # Test unknown flag.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'A,foo' ])
+
+ def test_assign_flag(self):
+ flags = FlagsDict(['A'], ['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test assigning an already assigned flag.
+ flags.assign_flag(FLAG_WHITELIST, set([ 'A' ]))
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test new additions.
+ flags.assign_flag(FLAG_GREYLIST, set([ 'A', 'B' ]))
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, 'B,' + FLAG_GREYLIST ])
+
+ # Test invalid API signature.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag(FLAG_WHITELIST, set([ 'C' ]))
+
+ # Test invalid flag.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag('foo', set([ 'A' ]))
if __name__ == '__main__':
unittest.main()
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 3f42275..faa3547 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -262,8 +262,9 @@
return false;
case FieldDescriptor::TYPE_STRING:
if (getPrivacyFlags(field).patterns_size() != 0) return false;
+ break;
default:
- continue;
+ break;
}
}
parents->erase(descriptor->full_name());
diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp
new file mode 100644
index 0000000..f597aab
--- /dev/null
+++ b/tools/powermodel/Android.bp
@@ -0,0 +1,26 @@
+
+java_library_host {
+ name: "powermodel",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "guava",
+ ],
+}
+
+java_test_host {
+ name: "powermodel-test",
+
+ test_suites: ["general-tests"],
+
+ srcs: ["test/**/*.java"],
+ java_resource_dirs: ["test-resource"],
+
+ static_libs: [
+ "powermodel",
+ "junit",
+ "mockito",
+ ],
+}
+
diff --git a/tools/powermodel/TEST_MAPPING b/tools/powermodel/TEST_MAPPING
new file mode 100644
index 0000000..c8db339
--- /dev/null
+++ b/tools/powermodel/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "powermodel-test"
+ }
+ ]
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ActivityReport.java b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
new file mode 100644
index 0000000..4a8f633
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * ActivityReport contains the summary of the activity that consumes power
+ * as reported by batterystats or statsd.
+ */
+public class ActivityReport {
+ private AppList<AppActivity> mApps;
+
+ public ImmutableList<AppActivity> getAllApps() {
+ return mApps.getAllApps();
+ }
+
+ public ImmutableList<AppActivity> getRegularApps() {
+ return mApps.getRegularApps();
+ }
+
+ public List<AppActivity> findApp(String pkg) {
+ return mApps.findApp(pkg);
+ }
+
+ public AppActivity findApp(SpecialApp specialApp) {
+ return mApps.findApp(specialApp);
+ }
+
+ /**
+ * Find a component in the GLOBAL app.
+ * <p>
+ * Returns null if either the global app doesn't exist (bad data?) or the component
+ * doesn't exist in the global app.
+ */
+ public ComponentActivity findGlobalComponent(Component component) {
+ final AppActivity global = mApps.findApp(SpecialApp.GLOBAL);
+ if (global == null) {
+ return null;
+ }
+ return global.getComponentActivity(component);
+ }
+
+ public static class Builder {
+ private AppList.Builder<AppActivity,AppActivity.Builder> mApps = new AppList.Builder();
+
+ public Builder() {
+ }
+
+ public ActivityReport build() {
+ final ActivityReport result = new ActivityReport();
+ result.mApps = mApps.build();
+ return result;
+ }
+
+ public void addActivity(Component component, Collection<ComponentActivity> activities) {
+ for (final ComponentActivity activity: activities) {
+ addActivity(component, activity);
+ }
+ }
+
+ public void addActivity(Component component, ComponentActivity activity) {
+ AppActivity.Builder app = mApps.get(activity.attribution);
+ if (app == null) {
+ app = new AppActivity.Builder();
+ app.setAttribution(activity.attribution);
+ mApps.put(activity.attribution, app);
+ }
+ app.addComponentActivity(component, activity);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppActivity.java b/tools/powermodel/src/com/android/powermodel/AppActivity.java
new file mode 100644
index 0000000..b87426c
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.HashMap;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class AppActivity extends AppInfo {
+
+ private ImmutableMap<Component, ComponentActivity> mComponents;
+ // TODO: power rails
+ // private ImmutableMap<Component, PowerRailActivity> mRails;
+
+ private AppActivity() {
+ }
+
+ /**
+ * Returns the {@link ComponentActivity} for the {@link Component} provided,
+ * or null if this AppActivity does not have that component.
+ * @more
+ * If there is no ComponentActivity for a particular Component, then
+ * there was no usage associated with that app for the app in question.
+ */
+ public ComponentActivity getComponentActivity(Component component) {
+ return mComponents.get(component);
+ }
+
+ public ImmutableSet<Component> getComponents() {
+ return mComponents.keySet();
+ }
+
+ public ImmutableMap<Component,ComponentActivity> getComponentActivities() {
+ return mComponents;
+ }
+
+ // TODO: power rails
+ // public ComponentActivity getPowerRail(Component component) {
+ // return mComponents.get(component);
+ // }
+ //
+ // public Set<Component> getPowerRails() {
+ // return mComponents.keySet();
+ // }
+
+ public static class Builder extends AppInfo.Builder<AppActivity> {
+ private HashMap<Component, ComponentActivity> mComponents = new HashMap();
+ // TODO power rails.
+
+ public Builder() {
+ }
+
+ public AppActivity build() {
+ final AppActivity result = new AppActivity();
+ init(result);
+ result.mComponents = ImmutableMap.copyOf(mComponents);
+ return result;
+ }
+
+ public void addComponentActivity(Component component, ComponentActivity activity) {
+ mComponents.put(component, activity);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppInfo.java b/tools/powermodel/src/com/android/powermodel/AppInfo.java
new file mode 100644
index 0000000..208339e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppInfo.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+class AppInfo {
+ private AttributionKey mAttribution;
+
+ protected AppInfo() {
+ }
+
+ public boolean hasPackage(String pkg) {
+ return mAttribution.hasPackage(pkg);
+ }
+
+ public AttributionKey getAttribution() {
+ return mAttribution;
+ }
+
+ abstract static class Builder<APP extends AppInfo> {
+ private AttributionKey mAttribution;
+
+ public Builder() {
+ }
+
+ public abstract APP build();
+
+ protected void init(AppInfo app) {
+ if (mAttribution == null) {
+ throw new RuntimeException("setAttribution(AttributionKey attribution) not called");
+ }
+ app.mAttribution = mAttribution;
+ }
+
+ public void setAttribution(AttributionKey attribution) {
+ mAttribution = attribution;
+ }
+
+ public AttributionKey getAttribution() {
+ return mAttribution;
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppList.java b/tools/powermodel/src/com/android/powermodel/AppList.java
new file mode 100644
index 0000000..19572fc
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppList.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+class AppList<APP extends AppInfo> {
+ private ImmutableList<APP> mAllApps;
+ private ImmutableList<APP> mRegularApps;
+ private ImmutableMap<SpecialApp,APP> mSpecialApps;
+
+ private AppList() {
+ }
+
+ public ImmutableList<APP> getAllApps() {
+ return mAllApps;
+ }
+
+ public ImmutableList<APP> getRegularApps() {
+ return mRegularApps;
+ }
+
+ public List<APP> findApp(String pkg) {
+ List<APP> result = new ArrayList();
+ for (APP app: mRegularApps) {
+ if (app.hasPackage(pkg)) {
+ result.add(app);
+ }
+ }
+ return result;
+ }
+
+ public APP findApp(SpecialApp specialApp) {
+ return mSpecialApps.get(specialApp);
+ }
+
+ public static class Builder<APP extends AppInfo, BUILDER extends AppInfo.Builder<APP>> {
+ private final HashMap<AttributionKey,BUILDER> mApps = new HashMap();
+
+ public Builder() {
+ }
+
+ public AppList<APP> build() {
+ final AppList<APP> result = new AppList();
+ final ArrayList<APP> allApps = new ArrayList();
+ final ArrayList<APP> regularApps = new ArrayList();
+ final HashMap<SpecialApp,APP> specialApps = new HashMap();
+ for (AppInfo.Builder<APP> app: mApps.values()) {
+ final AttributionKey attribution = app.getAttribution();
+ final APP appActivity = app.build();
+ allApps.add(appActivity);
+ if (attribution.isSpecialApp()) {
+ specialApps.put(attribution.getSpecialApp(), appActivity);
+ } else {
+ regularApps.add(appActivity);
+ }
+ }
+ result.mAllApps = ImmutableList.copyOf(allApps);
+ result.mRegularApps = ImmutableList.copyOf(regularApps);
+ result.mSpecialApps = ImmutableMap.copyOf(specialApps);
+ return result;
+ }
+
+ public BUILDER get(AttributionKey attribution) {
+ return mApps.get(attribution);
+ }
+
+ public BUILDER put(AttributionKey attribution, BUILDER app) {
+ return mApps.put(attribution, app);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppPower.java b/tools/powermodel/src/com/android/powermodel/AppPower.java
new file mode 100644
index 0000000..283982b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppPower.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.powermodel;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+public class AppPower extends AppInfo {
+ private ImmutableMap<Component, ComponentPower> mComponents;
+
+ private double mAppPowerMah;
+
+
+ private AppPower() {
+ }
+
+ /**
+ * Returns the {@link ComponentPower} for the {@link Component} provided,
+ * or null if this AppPower does not have that component.
+ * @more
+ * If the component was in the power profile for this device, there
+ * will be a component for it, even if there was no power used
+ * by that component. In that case, the
+ * {@link ComponentPower.getUsage() ComponentPower.getUsage()}
+ * method will return 0.
+ */
+ public ComponentPower getComponentPower(Component component) {
+ return mComponents.get(component);
+ }
+
+ public Set<Component> getComponents() {
+ return mComponents.keySet();
+ }
+
+ /**
+ * Return the total power used by this app.
+ */
+ public double getAppPowerMah() {
+ return mAppPowerMah;
+ }
+
+ /**
+ * Builder class for {@link AppPower}
+ */
+ public static class Builder extends AppInfo.Builder<AppPower> {
+ private HashMap<Component, ComponentPower> mComponents = new HashMap();
+
+ public Builder() {
+ }
+
+ public AppPower build() {
+ final AppPower result = new AppPower();
+ init(result);
+ result.mComponents = ImmutableMap.copyOf(mComponents);
+
+ // Add up the components
+ double appPowerMah = 0;
+ for (final ComponentPower componentPower: mComponents.values()) {
+ appPowerMah += componentPower.powerMah;
+ }
+ result.mAppPowerMah = appPowerMah;
+
+ return result;
+ }
+
+ public void addComponentPower(Component component, ComponentPower componentPower) {
+ mComponents.put(component, componentPower);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AttributionKey.java b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
new file mode 100644
index 0000000..f19e0b7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AttributionKey.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.powermodel;
+
+import java.util.Set;
+import java.util.HashSet;
+
+import com.google.common.collect.ImmutableSet;
+
+public class AttributionKey {
+ private final int mUid;
+ private final ImmutableSet<String> mPackages;
+ private final SpecialApp mSpecialApp;
+
+ public AttributionKey(SpecialApp specialApp) {
+ mUid = -1;
+ mPackages = ImmutableSet.of();
+ mSpecialApp = specialApp;
+ }
+
+ public AttributionKey(int uid, Set<String> packages) {
+ mUid = uid;
+ mPackages = ImmutableSet.copyOf(packages);
+ mSpecialApp = null;
+ }
+
+ public ImmutableSet<String> getPackages() {
+ return mPackages;
+ }
+
+ public boolean hasPackage(String pkg) {
+ return mPackages.contains(pkg);
+ }
+
+ public SpecialApp getSpecialApp() {
+ return mSpecialApp;
+ }
+
+ public boolean isSpecialApp() {
+ return mSpecialApp != null;
+ }
+
+ /**
+ * Returns the uid for this attribution, or -1 if there isn't one
+ * (e.g. if it is a special app).
+ */
+ public int getUid() {
+ return mUid;
+ }
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = (31 * hash) + (mUid);
+ hash = (31 * hash) + (mPackages == null ? 0 : mPackages.hashCode());
+ hash = (31 * hash) + (mSpecialApp == null ? 0 : mSpecialApp.hashCode());
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null) {
+ return false;
+ }
+ if (this.getClass() != o.getClass()) {
+ return false;
+ }
+ final AttributionKey that = (AttributionKey)o;
+ return (this.mUid == that.mUid)
+ && this.mPackages != null && this.mPackages.equals(that.mPackages)
+ && this.mSpecialApp != null && this.mSpecialApp.equals(that.mSpecialApp);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder str = new StringBuilder("AttributionKey(");
+ if (mUid >= 0) {
+ str.append(" uid=");
+ str.append(mUid);
+ }
+ if (mPackages.size() > 0) {
+ str.append(" packages=[");
+ for (String pkg: mPackages) {
+ str.append(' ');
+ str.append(pkg);
+ }
+ str.append(" ]");
+ }
+ if (mSpecialApp != null) {
+ str.append(" specialApp=");
+ str.append(mSpecialApp.name());
+ }
+ str.append(" )");
+ return str.toString();
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
new file mode 100644
index 0000000..595c661
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import com.android.powermodel.component.ModemBatteryStatsReader;
+
+public class BatteryStatsReader {
+ /**
+ * Construct a reader.
+ */
+ public BatteryStatsReader() {
+ }
+
+ /**
+ * Parse a powermodel.xml file and return a PowerProfile object.
+ *
+ * @param stream An InputStream containing the batterystats output.
+ *
+ * @throws ParseException Thrown when the xml file can not be parsed.
+ * @throws IOException When there is a problem reading the stream.
+ */
+ public static ActivityReport parse(InputStream stream) throws ParseException, IOException {
+ final Parser parser = new Parser(stream);
+ return parser.parse();
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ final InputStream mStream;
+ final ActivityReport mResult;
+ RawBatteryStats mBs;
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new ActivityReport();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ ActivityReport parse() throws ParseException, IOException {
+ mBs = RawBatteryStats.parse(mStream);
+
+ final ActivityReport.Builder report = new ActivityReport.Builder();
+
+ report.addActivity(Component.MODEM, ModemBatteryStatsReader.createActivities(mBs));
+
+ return report.build();
+ }
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/Component.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/Component.java
index 27d25b8..baae6d7 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/Component.java
@@ -13,12 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.powermodel;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
+ * The hardware components that use power on a device.
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public enum Component {
+ CPU,
+ SCREEN,
+ MODEM,
+ WIFI,
+ BLUETOOTH,
+ VIDEO,
+ AUDIO,
+ FLASHLIGHT,
+ CAMERA,
+ GPS,
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentActivity.java b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
new file mode 100644
index 0000000..c1e2662
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+
+/**
+ * Encapsulates the work done by an app (including synthetic apps) that costs power.
+ */
+public class ComponentActivity {
+ public AttributionKey attribution;
+
+ protected ComponentActivity(AttributionKey attribution) {
+ this.attribution = attribution;
+ }
+
+ // TODO: Can we refactor what goes into the activities so this function
+ // doesn't need the global state?
+ /**
+ * Apply the power profile for this component. Subclasses should implement this
+ * to do the per-component calculatinos. The default implementation returns null.
+ * If this method returns null, then there will be no power associated for this
+ * component, which, for example is true with some of the GLOBAL activities.
+ */
+ public ComponentPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ return null;
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentPower.java b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
new file mode 100644
index 0000000..b22ff87
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+/**
+ * The hardware component that uses power on a device.
+ * <p>
+ * This base class contains the total power used by each Component in an app.
+ * Subclasses may add more detail, which is a drill-down, but is not to be
+ * <i>added</i> to {@link #powerMah}.
+ */
+public abstract class ComponentPower<ACTIVITY extends ComponentActivity> {
+ /**
+ * The app associated with this ComponentPower.
+ */
+ public AttributionKey attribution;
+
+ /**
+ * The app activity that resulted in the power usage for this component.
+ */
+ public ACTIVITY activity;
+
+ /**
+ * The total power used by this component in this app.
+ */
+ public double powerMah;
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
similarity index 89%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to tools/powermodel/src/com/android/powermodel/ComponentProfile.java
index c66a6cb..e76e5fb 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package com.android.powermodel;
-parcelable ContentCaptureEvent;
+public class ComponentProfile {
+}
diff --git a/tools/powermodel/src/com/android/powermodel/CsvParser.java b/tools/powermodel/src/com/android/powermodel/CsvParser.java
new file mode 100644
index 0000000..78cd261
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/CsvParser.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+/**
+ * Parses CSV.
+ * <p>
+ * Call parse() with an InputStream.
+ * <p>
+ * CsvLineProcessor.onLine() will be called for each line in the source document.
+ * <p>
+ * To simplify parsing and to protect against using too much memory for bad
+ * data, the maximum field length is {@link #MAX_FIELD_SIZE}.
+ */
+class CsvParser {
+ /**
+ * The maximum size of a single field in bytes.
+ */
+ public static final int MAX_FIELD_SIZE = (8*1024)-1;
+
+ /**
+ * Callback interface for each line of CSV as it is parsed.
+ */
+ interface LineProcessor {
+ /**
+ * A line of CSV was parsed.
+ *
+ * @param lineNumber the line number in the file, starting at 1
+ * @param fields the comma separated fields for the line
+ */
+ void onLine(int lineNumber, ArrayList<String> fields) throws ParseException;
+ }
+
+ /**
+ * Parse the CSV text in input, calling onto processor for each row.
+ */
+ public static void parse(InputStream input, LineProcessor processor)
+ throws IOException, ParseException {
+ final Charset utf8 = StandardCharsets.UTF_8;
+ final byte[] buf = new byte[MAX_FIELD_SIZE+1];
+ int lineNumber = 1;
+ int readPos = 0;
+ int prev = 0;
+ ArrayList<String> fields = new ArrayList<String>();
+ boolean finalBuffer = false;
+ boolean escaping = false;
+ boolean sawQuote = false;
+
+ while (!finalBuffer) {
+ int amt = input.read(buf, readPos, buf.length-readPos);
+ if (amt < 0) {
+ // No more data. Process whatever's left from before.
+ amt = readPos;
+ finalBuffer = true;
+ } else {
+ // Process whatever's left from before, plus the new data.
+ amt += readPos;
+ finalBuffer = false;
+ }
+
+ // Process as much of this buffer as we can.
+ int fieldStart = 0;
+ int index = readPos;
+ int escapeIndex = escaping ? readPos : -1;
+ while (index < amt) {
+ byte c = buf[index];
+ if (c == '\r' || c == '\n') {
+ if (escaping) {
+ // TODO: Quotes do not escape newlines in our CSV dialect,
+ // but we actually see some data where it should.
+ fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+ escapeIndex = -1;
+ escaping = false;
+ sawQuote = false;
+ } else {
+ fields.add(new String(buf, fieldStart, index-fieldStart));
+ }
+ // Don't report blank lines
+ if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+ processor.onLine(lineNumber, fields);
+ }
+ fields = new ArrayList<String>();
+ if (!(c == '\n' && prev == '\r')) {
+ // Don't double increment for dos line endings.
+ lineNumber++;
+ }
+ fieldStart = index = index + 1;
+ } else {
+ if (escaping) {
+ // Field started with a " so quotes are escaped with " and commas
+ // don't matter except when following a single quote.
+ if (c == '"') {
+ if (sawQuote) {
+ buf[escapeIndex] = buf[index];
+ escapeIndex++;
+ sawQuote = false;
+ } else {
+ sawQuote = true;
+ }
+ index++;
+ } else if (sawQuote && c == ',') {
+ fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+ fieldStart = index = index + 1;
+ escapeIndex = -1;
+ escaping = false;
+ sawQuote = false;
+ } else {
+ buf[escapeIndex] = buf[index];
+ escapeIndex++;
+ index++;
+ sawQuote = false;
+ }
+ } else {
+ if (c == ',') {
+ fields.add(new String(buf, fieldStart, index-fieldStart));
+ fieldStart = index + 1;
+ } else if (c == '"' && fieldStart == index) {
+ // First character is a "
+ escaping = true;
+ fieldStart = escapeIndex = index + 1;
+ }
+ index++;
+ }
+ }
+ prev = c;
+ }
+
+ // A single field is greater than buf.length, so fail.
+ if (fieldStart == 0 && index == buf.length) {
+ throw new ParseException(lineNumber, "Line is too long: "
+ + new String(buf, 0, 20, utf8) + "...");
+ }
+
+ // Move whatever we didn't process to the beginning of the buffer
+ // and try again.
+ if (fieldStart != amt) {
+ readPos = (escaping ? escapeIndex : index) - fieldStart;
+ System.arraycopy(buf, fieldStart, buf, 0, readPos);
+ } else {
+ readPos = 0;
+ }
+
+ // Process whatever's left over
+ if (finalBuffer) {
+ fields.add(new String(buf, 0, readPos));
+ // If there is any content, return the last line.
+ if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+ processor.onLine(lineNumber, fields);
+ }
+ }
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/ParseException.java b/tools/powermodel/src/com/android/powermodel/ParseException.java
new file mode 100644
index 0000000..e1f232b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ParseException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+public class ParseException extends Exception {
+ public final int line;
+
+ public ParseException(int line, String message, Throwable th) {
+ super(message, th);
+ this.line = line;
+ }
+
+ public ParseException(int line, String message) {
+ this(line, message, null);
+ }
+
+ public ParseException(String message) {
+ this(0, message, null);
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/PowerProfile.java b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
new file mode 100644
index 0000000..373a9c9
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import com.android.powermodel.util.Conversion;
+
+public class PowerProfile {
+
+ // Remaining fields from the android code for which the actual usage is unclear.
+ // battery.capacity
+ // bluetooth.controller.voltage
+ // modem.controller.voltage
+ // gps.voltage
+ // wifi.controller.voltage
+ // radio.on
+ // radio.scanning
+ // radio.active
+ // memory.bandwidths
+ // wifi.batchedscan
+ // wifi.scan
+ // wifi.on
+ // wifi.active
+ // wifi.controller.tx_levels
+
+ private static Pattern RE_CLUSTER_POWER = Pattern.compile("cpu.cluster_power.cluster([0-9]*)");
+ private static Pattern RE_CORE_SPEEDS = Pattern.compile("cpu.core_speeds.cluster([0-9]*)");
+ private static Pattern RE_CORE_POWER = Pattern.compile("cpu.core_power.cluster([0-9]*)");
+
+ private HashMap<Component, ComponentProfile> mComponents = new HashMap();
+
+ /**
+ * Which element we are currently parsing.
+ */
+ enum ElementState {
+ BEGIN,
+ TOP,
+ ITEM,
+ ARRAY,
+ VALUE
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ private final InputStream mStream;
+ private final PowerProfile mResult;
+
+ // Builders for the ComponentProfiles.
+ private final AudioProfile mAudio = new AudioProfile();
+ private final BluetoothProfile mBluetooth = new BluetoothProfile();
+ private final CameraProfile mCamera = new CameraProfile();
+ private final CpuProfile.Builder mCpuBuilder = new CpuProfile.Builder();
+ private final FlashlightProfile mFlashlight = new FlashlightProfile();
+ private final GpsProfile.Builder mGpsBuilder = new GpsProfile.Builder();
+ private final ModemProfile.Builder mModemBuilder = new ModemProfile.Builder();
+ private final ScreenProfile mScreen = new ScreenProfile();
+ private final VideoProfile mVideo = new VideoProfile();
+ private final WifiProfile mWifi = new WifiProfile();
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new PowerProfile();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ PowerProfile parse() throws ParseException {
+ final SAXParserFactory factory = SAXParserFactory.newInstance();
+ AndroidResourceHandler handler = null;
+ try {
+ final SAXParser saxParser = factory.newSAXParser();
+
+ handler = new AndroidResourceHandler() {
+ @Override
+ public void onItem(Locator locator, String name, float value)
+ throws SAXParseException {
+ Parser.this.onItem(locator, name, value);
+ }
+
+ @Override
+ public void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException {
+ Parser.this.onArray(locator, name, value);
+ }
+ };
+
+ saxParser.parse(mStream, handler);
+ } catch (ParserConfigurationException ex) {
+ // Coding error, not runtime error.
+ throw new RuntimeException(ex);
+ } catch (SAXParseException ex) {
+ throw new ParseException(ex.getLineNumber(), ex.getMessage(), ex);
+ } catch (SAXException | IOException ex) {
+ // Make a guess about the line number.
+ throw new ParseException(handler.getLineNumber(), ex.getMessage(), ex);
+ }
+
+ // TODO: This doesn't cover the multiple algorithms. Some refactoring will
+ // be necessary.
+ mResult.mComponents.put(Component.AUDIO, mAudio);
+ mResult.mComponents.put(Component.BLUETOOTH, mBluetooth);
+ mResult.mComponents.put(Component.CAMERA, mCamera);
+ mResult.mComponents.put(Component.CPU, mCpuBuilder.build());
+ mResult.mComponents.put(Component.FLASHLIGHT, mFlashlight);
+ mResult.mComponents.put(Component.GPS, mGpsBuilder.build());
+ mResult.mComponents.put(Component.MODEM, mModemBuilder.build());
+ mResult.mComponents.put(Component.SCREEN, mScreen);
+ mResult.mComponents.put(Component.VIDEO, mVideo);
+ mResult.mComponents.put(Component.WIFI, mWifi);
+
+ return mResult;
+ }
+
+ /**
+ * Handles an item tag in the power_profile.xml.
+ */
+ public void onItem(Locator locator, String name, float value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("ambient.on".equals(name)) {
+ mScreen.ambientMa = value;
+ } else if ("audio".equals(name)) {
+ mAudio.onMa = value;
+ } else if ("bluetooth.controller.idle".equals(name)) {
+ mBluetooth.idleMa = value;
+ } else if ("bluetooth.controller.rx".equals(name)) {
+ mBluetooth.rxMa = value;
+ } else if ("bluetooth.controller.tx".equals(name)) {
+ mBluetooth.txMa = value;
+ } else if ("camera.avg".equals(name)) {
+ mCamera.onMa = value;
+ } else if ("camera.flashlight".equals(name)) {
+ mFlashlight.onMa = value;
+ } else if ("cpu.suspend".equals(name)) {
+ mCpuBuilder.setSuspendMa(value);
+ } else if ("cpu.idle".equals(name)) {
+ mCpuBuilder.setIdleMa(value);
+ } else if ("cpu.active".equals(name)) {
+ mCpuBuilder.setActiveMa(value);
+ } else if ((index = matchIndexedRegex(locator, RE_CLUSTER_POWER, name)) != null) {
+ mCpuBuilder.setClusterPower(index, value);
+ } else if ("gps.on".equals(name)) {
+ mGpsBuilder.setOnMa(value);
+ } else if ("modem.controller.sleep".equals(name)) {
+ mModemBuilder.setSleepMa(value);
+ } else if ("modem.controller.idle".equals(name)) {
+ mModemBuilder.setIdleMa(value);
+ } else if ("modem.controller.rx".equals(name)) {
+ mModemBuilder.setRxMa(value);
+ } else if ("radio.scanning".equals(name)) {
+ mModemBuilder.setScanningMa(value);
+ } else if ("screen.on".equals(name)) {
+ mScreen.onMa = value;
+ } else if ("screen.full".equals(name)) {
+ mScreen.fullMa = value;
+ } else if ("video".equals(name)) {
+ mVideo.onMa = value;
+ } else if ("wifi.controller.idle".equals(name)) {
+ mWifi.idleMa = value;
+ } else if ("wifi.controller.rx".equals(name)) {
+ mWifi.rxMa = value;
+ } else if ("wifi.controller.tx".equals(name)) {
+ mWifi.txMa = value;
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+
+ /**
+ * Handles an array tag in the power_profile.xml.
+ */
+ public void onArray(Locator locator, String name, float[] value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("cpu.clusters.cores".equals(name)) {
+ mCpuBuilder.setCoreCount(Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_SPEEDS, name)) != null) {
+ mCpuBuilder.setCoreSpeeds(index, Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_POWER, name)) != null) {
+ mCpuBuilder.setCorePower(index, value);
+ } else if ("gps.signalqualitybased".equals(name)) {
+ mGpsBuilder.setSignalMa(value);
+ } else if ("modem.controller.tx".equals(name)) {
+ mModemBuilder.setTxMa(value);
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+ }
+
+ /**
+ * SAX XML handler that can parse the android resource files.
+ * In our case, all elements are floats.
+ */
+ abstract static class AndroidResourceHandler extends DefaultHandler {
+ /**
+ * The set of names already processed. Map of name to line number.
+ */
+ private HashMap<String,Integer> mAlreadySeen = new HashMap<String,Integer>();
+
+ /**
+ * Where in the document we are parsing.
+ */
+ private Locator mLocator;
+
+ /**
+ * Which element we are currently parsing.
+ */
+ private ElementState mState = ElementState.BEGIN;
+
+ /**
+ * Saved name from item and array elements.
+ */
+ private String mName;
+
+ /**
+ * The text that is currently being captured, or null if {@link #startCapturingText()}
+ * has not been called.
+ */
+ private StringBuilder mText;
+
+ /**
+ * The array values that have been parsed so for for this array. Null if we are
+ * not inside an array tag.
+ */
+ private ArrayList<Float> mArray;
+
+ /**
+ * Called when an item tag is encountered.
+ */
+ public abstract void onItem(Locator locator, String name, float value)
+ throws SAXParseException;
+
+ /**
+ * Called when an array is encountered.
+ */
+ public abstract void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException;
+
+ /**
+ * If we have a Locator set, return the line number, otherwise return 0.
+ */
+ public int getLineNumber() {
+ return mLocator != null ? mLocator.getLineNumber() : 0;
+ }
+
+ /**
+ * Handle setting the parse location object.
+ */
+ public void setDocumentLocator(Locator locator) {
+ mLocator = locator;
+ }
+
+ /**
+ * Handle beginning of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void startElement(String ns, String ln, String element,
+ Attributes attr) throws SAXException {
+ switch (mState) {
+ case BEGIN:
+ // Outer element, we don't care the tag name.
+ mState = ElementState.TOP;
+ return;
+ case TOP:
+ if ("item".equals(element)) {
+ mState = ElementState.ITEM;
+ saveNameAttribute(attr);
+ startCapturingText();
+ return;
+ } else if ("array".equals(element)) {
+ mState = ElementState.ARRAY;
+ mArray = new ArrayList<Float>();
+ saveNameAttribute(attr);
+ return;
+ }
+ break;
+ case ARRAY:
+ if ("value".equals(element)) {
+ mState = ElementState.VALUE;
+ startCapturingText();
+ return;
+ }
+ break;
+ }
+ throw new SAXParseException("unexpected element: '" + element + "'", mLocator);
+ }
+
+ /**
+ * Handle end of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void endElement(String ns, String ln, String element) throws SAXException {
+ switch (mState) {
+ case ITEM: {
+ float value = parseFloat(finishCapturingText());
+ mState = ElementState.TOP;
+ onItem(mLocator, mName, value);
+ break;
+ }
+ case ARRAY: {
+ final int N = mArray.size();
+ float[] values = new float[N];
+ for (int i=0; i<N; i++) {
+ values[i] = mArray.get(i);
+ }
+ mArray = null;
+ mState = ElementState.TOP;
+ onArray(mLocator, mName, values);
+ break;
+ }
+ case VALUE: {
+ mArray.add(parseFloat(finishCapturingText()));
+ mState = ElementState.ARRAY;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Interstitial text received.
+ *
+ * @throws SAXException if there shouldn't be non-whitespace text here
+ */
+ @Override
+ public void characters(char text[], int start, int length) throws SAXException {
+ if (mText == null && length > 0 && !isWhitespace(text, start, length)) {
+ throw new SAXParseException("unexpected text: '"
+ + firstLine(text, start, length).trim() + "'", mLocator);
+ }
+ if (mText != null) {
+ mText.append(text, start, length);
+ }
+ }
+
+ /**
+ * Begin collecting text from inside an element.
+ */
+ private void startCapturingText() {
+ if (mText != null) {
+ throw new RuntimeException("ASSERTION FAILED: Shouldn't be already capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ mText = new StringBuilder();
+ }
+
+ /**
+ * Stop capturing text from inside an element.
+ *
+ * @return the captured text
+ */
+ private String finishCapturingText() {
+ if (mText == null) {
+ throw new RuntimeException("ASSERTION FAILED: Should already be capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ final String result = mText.toString().trim();
+ mText = null;
+ return result;
+ }
+
+ /**
+ * Get the "name" attribute.
+ *
+ * @throws SAXParseException if the name attribute is not present or if
+ * the name has already been seen in the file.
+ */
+ private void saveNameAttribute(Attributes attr) throws SAXParseException {
+ final String name = attr.getValue("name");
+ if (name == null) {
+ throw new SAXParseException("expected 'name' attribute", mLocator);
+ }
+ Integer prev = mAlreadySeen.put(name, mLocator.getLineNumber());
+ if (prev != null) {
+ throw new SAXParseException("name '" + name + "' already seen on line: " + prev,
+ mLocator);
+ }
+ mName = name;
+ }
+
+ /**
+ * Gets the float value of the string.
+ *
+ * @throws SAXParseException if 'text' can't be parsed as a float.
+ */
+ private float parseFloat(String text) throws SAXParseException {
+ try {
+ return Float.parseFloat(text);
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("not a valid float value: '" + text + "'",
+ mLocator, ex);
+ }
+ }
+ }
+
+ /**
+ * Return whether the given substring is all whitespace.
+ */
+ private static boolean isWhitespace(char[] text, int start, int length) {
+ for (int i = start; i < (start + length); i++) {
+ if (!Character.isSpace(text[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the contents of text up to the first newline.
+ */
+ private static String firstLine(char[] text, int start, int length) {
+ // TODO: The line number will be wrong if we skip preceeding blank lines.
+ while (length > 0) {
+ if (Character.isSpace(text[start])) {
+ start++;
+ length--;
+ }
+ }
+ int newlen = 0;
+ for (; newlen < length; newlen++) {
+ final char c = text[newlen];
+ if (c == '\n' || c == '\r') {
+ break;
+ }
+ }
+ return new String(text, start, newlen);
+ }
+
+ /**
+ * If the pattern matches, return the first group of that as an Integer.
+ * If not return null.
+ */
+ private static Integer matchIndexedRegex(Locator locator, Pattern pattern, String text)
+ throws SAXParseException {
+ final Matcher m = pattern.matcher(text);
+ if (m.matches()) {
+ try {
+ return Integer.parseInt(m.group(1));
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("Invalid field name: '" + text + "'", locator, ex);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public static PowerProfile parse(InputStream stream) throws ParseException {
+ return (new Parser(stream)).parse();
+ }
+
+ private PowerProfile() {
+ }
+
+ public ComponentProfile getComponent(Component component) {
+ return mComponents.get(component);
+ }
+
+}
diff --git a/tools/powermodel/src/com/android/powermodel/PowerReport.java b/tools/powermodel/src/com/android/powermodel/PowerReport.java
new file mode 100644
index 0000000..76ba672
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerReport.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.powermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * PowerReport contains the summary of all power used on a device
+ * as reported by batterystats or statsd, based on the power profile.
+ */
+public class PowerReport {
+ private AppList<AppPower> mApps;
+ private double mTotalPowerMah;
+
+ private PowerReport() {
+ }
+
+ /**
+ * The total power used by this device for this PowerReport.
+ */
+ public double getTotalPowerMah() {
+ return mTotalPowerMah;
+ }
+
+ public List<AppPower> getAllApps() {
+ return mApps.getAllApps();
+ }
+
+ public List<AppPower> getRegularApps() {
+ return mApps.getRegularApps();
+ }
+
+ public List<AppPower> findApp(String pkg) {
+ return mApps.findApp(pkg);
+ }
+
+ public AppPower findApp(SpecialApp specialApp) {
+ return mApps.findApp(specialApp);
+ }
+
+ public static PowerReport createReport(PowerProfile profile, ActivityReport activityReport) {
+ final PowerReport.Builder powerReport = new PowerReport.Builder();
+ for (final AppActivity appActivity: activityReport.getAllApps()) {
+ final AppPower.Builder appPower = new AppPower.Builder();
+ appPower.setAttribution(appActivity.getAttribution());
+
+ for (final ImmutableMap.Entry<Component,ComponentActivity> entry:
+ appActivity.getComponentActivities().entrySet()) {
+ final ComponentPower componentPower = entry.getValue()
+ .applyProfile(activityReport, profile);
+ if (componentPower != null) {
+ appPower.addComponentPower(entry.getKey(), componentPower);
+ }
+ }
+
+ powerReport.add(appPower);
+ }
+ return powerReport.build();
+ }
+
+ private static class Builder {
+ private AppList.Builder mApps = new AppList.Builder();
+
+ public Builder() {
+ }
+
+ public PowerReport build() {
+ final PowerReport report = new PowerReport();
+
+ report.mApps = mApps.build();
+
+ for (AppPower app: report.mApps.getAllApps()) {
+ report.mTotalPowerMah += app.getAppPowerMah();
+ }
+
+ return report;
+ }
+
+ public void add(AppPower.Builder app) {
+ mApps.put(app.getAttribution(), app);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
new file mode 100644
index 0000000..76c0482
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RawBatteryStats {
+ /**
+ * The factory objects for the records. Initialized in the static block.
+ */
+ private static HashMap<String,RecordFactory> sFactories
+ = new HashMap<String,RecordFactory>();
+
+ /**
+ * The Record objects that have been parsed.
+ */
+ private ArrayList<Record> mRecords = new ArrayList<Record>();
+
+ /**
+ * The Record objects that have been parsed, indexed by type.
+ *
+ * Don't use this before {@link #indexRecords()} has been called.
+ */
+ private ImmutableMap<String,ImmutableList<Record>> mRecordsByType;
+
+ /**
+ * The attribution keys for which we have data (corresponding to UIDs we've seen).
+ * <p>
+ * Does not include the synthetic apps.
+ * <p>
+ * Don't use this before {@link #indexRecords()} has been called.
+ */
+ private ImmutableSet<AttributionKey> mApps;
+
+ /**
+ * The warnings that have been issued during parsing.
+ */
+ private ArrayList<Warning> mWarnings = new ArrayList<Warning>();
+
+ /**
+ * The version of the BatteryStats dumpsys that we are using. This value
+ * is set to -1 initially, and then when parsing the (hopefully) first
+ * line, 'vers', it is set to the correct version.
+ */
+ private int mDumpsysVersion = -1;
+
+ /**
+ * Enum used in the Line annotation to mark whether a field is expected to be
+ * system-wide or scoped to an app.
+ */
+ public enum Scope {
+ SYSTEM,
+ UID
+ }
+
+ /**
+ * Enum used to indicated the expected number of results.
+ */
+ public enum Count {
+ SINGLE,
+ MULTIPLE
+ }
+
+ /**
+ * Annotates classes that represent a line of CSV in the batterystats CSV
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ @interface Line {
+ String tag();
+ Scope scope();
+ Count count();
+ }
+
+ /**
+ * Annotates fields that should be parsed automatically from CSV
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface Field {
+ /**
+ * The "column" of this field in the most recent version of the CSV.
+ * When parsing old versions, fields that were added will be automatically
+ * removed and the indices will be fixed up.
+ *
+ * The header fields (version, uid, category, type) will be automatically
+ * handled for the base Line type. The index 0 should start after those.
+ */
+ int index();
+
+ /**
+ * First version that this field appears in.
+ */
+ int added() default 0;
+ }
+
+ /**
+ * Each line in the BatteryStats CSV is tagged with a category, that says
+ * which of the time collection modes was used for the data.
+ */
+ public enum Category {
+ INFO("i"),
+ LAST("l"),
+ UNPLUGGED("u"),
+ CURRENT("c");
+
+ public final String tag;
+
+ Category(String tag) {
+ this.tag = tag;
+ }
+ }
+
+ /**
+ * Base class for all lines in a batterystats CSV file.
+ */
+ public static class Record {
+ /**
+ * Whether all of the fields for the indicated version of this record
+ * have been filled in.
+ */
+ public boolean complete;
+
+
+ @Field(index=-4)
+ public int lineVersion;
+
+ @Field(index=-3)
+ public int uid;
+
+ @Field(index=-2)
+ public Category category;
+
+ @Field(index=-1)
+ public String lineType;
+ }
+
+ @Line(tag="bt", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Battery extends Record {
+ // If which != STATS_SINCE_CHARGED, the csv will be "N/A" and we will get
+ // a parsing warning. Nobody uses anything other than STATS_SINCE_CHARGED.
+ @Field(index=0)
+ public int startCount;
+
+ @Field(index=1)
+ public long whichBatteryRealtimeMs;
+
+ @Field(index=2)
+ public long whichBatteryUptimeMs;
+
+ @Field(index=3)
+ public long totalRealtimeMs;
+
+ @Field(index=4)
+ public long totalUptimeMs;
+
+ @Field(index=5)
+ public long getStartClockTimeMs;
+
+ @Field(index=6)
+ public long whichBatteryScreenOffRealtimeMs;
+
+ @Field(index=7)
+ public long whichBatteryScreenOffUptimeMs;
+
+ @Field(index=8)
+ public long estimatedBatteryCapacityMah;
+
+ @Field(index=9)
+ public long minLearnedBatteryCapacityMah;
+
+ @Field(index=10)
+ public long maxLearnedBatteryCapacityMah;
+
+ @Field(index=11)
+ public long screenDozeTimeMs;
+ }
+
+ @Line(tag="gn", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class GlobalNetwork extends Record {
+ @Field(index=0)
+ public long mobileRxTotalBytes;
+
+ @Field(index=1)
+ public long mobileTxTotalBytes;
+
+ @Field(index=2)
+ public long wifiRxTotalBytes;
+
+ @Field(index=3)
+ public long wifiTxTotalBytes;
+
+ @Field(index=4)
+ public long mobileRxTotalPackets;
+
+ @Field(index=5)
+ public long mobileTxTotalPackets;
+
+ @Field(index=6)
+ public long wifiRxTotalPackets;
+
+ @Field(index=7)
+ public long wifiTxTotalPackets;
+
+ @Field(index=8)
+ public long btRxTotalBytes;
+
+ @Field(index=9)
+ public long btTxTotalBytes;
+ }
+
+ @Line(tag="gmcd", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class GlobalModemController extends Record {
+ @Field(index=0)
+ public long idleMs;
+
+ @Field(index=1)
+ public long rxTimeMs;
+
+ @Field(index=2)
+ public long powerMaMs;
+
+ @Field(index=3)
+ public long[] txTimeMs;
+ }
+
+ @Line(tag="m", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Misc extends Record {
+ @Field(index=0)
+ public long screenOnTimeMs;
+
+ @Field(index=1)
+ public long phoneOnTimeMs;
+
+ @Field(index=2)
+ public long fullWakeLockTimeTotalMs;
+
+ @Field(index=3)
+ public long partialWakeLockTimeTotalMs;
+
+ @Field(index=4)
+ public long mobileRadioActiveTimeMs;
+
+ @Field(index=5)
+ public long mobileRadioActiveAdjustedTimeMs;
+
+ @Field(index=6)
+ public long interactiveTimeMs;
+
+ @Field(index=7)
+ public long powerSaveModeEnabledTimeMs;
+
+ @Field(index=8)
+ public int connectivityChangeCount;
+
+ @Field(index=9)
+ public long deepDeviceIdleModeTimeMs;
+
+ @Field(index=10)
+ public long deepDeviceIdleModeCount;
+
+ @Field(index=11)
+ public long deepDeviceIdlingTimeMs;
+
+ @Field(index=12)
+ public long deepDeviceIdlingCount;
+
+ @Field(index=13)
+ public long mobileRadioActiveCount;
+
+ @Field(index=14)
+ public long mobileRadioActiveUnknownTimeMs;
+
+ @Field(index=15)
+ public long lightDeviceIdleModeTimeMs;
+
+ @Field(index=16)
+ public long lightDeviceIdleModeCount;
+
+ @Field(index=17)
+ public long lightDeviceIdlingTimeMs;
+
+ @Field(index=18)
+ public long lightDeviceIdlingCount;
+
+ @Field(index=19)
+ public long lightLongestDeviceIdleModeTimeMs;
+
+ @Field(index=20)
+ public long deepLongestDeviceIdleModeTimeMs;
+ }
+
+ @Line(tag="nt", scope=Scope.UID, count=Count.SINGLE)
+ public static class Network extends Record {
+ @Field(index=0)
+ public long mobileRxBytes;
+
+ @Field(index=1)
+ public long mobileTxBytes;
+
+ @Field(index=2)
+ public long wifiRxBytes;
+
+ @Field(index=3)
+ public long wifiTxBytes;
+
+ @Field(index=4)
+ public long mobileRxPackets;
+
+ @Field(index=5)
+ public long mobileTxPackets;
+
+ @Field(index=6)
+ public long wifiRxPackets;
+
+ @Field(index=7)
+ public long wifiTxPackets;
+
+ // This is microseconds, because... batterystats.
+ @Field(index=8)
+ public long mobileRadioActiveTimeUs;
+
+ @Field(index=9)
+ public long mobileRadioActiveCount;
+
+ @Field(index=10)
+ public long btRxBytes;
+
+ @Field(index=11)
+ public long btTxBytes;
+
+ @Field(index=12)
+ public long mobileWakeupCount;
+
+ @Field(index=13)
+ public long wifiWakeupCount;
+
+ @Field(index=14)
+ public long mobileBgRxBytes;
+
+ @Field(index=15)
+ public long mobileBgTxBytes;
+
+ @Field(index=16)
+ public long wifiBgRxBytes;
+
+ @Field(index=17)
+ public long wifiBgTxBytes;
+
+ @Field(index=18)
+ public long mobileBgRxPackets;
+
+ @Field(index=19)
+ public long mobileBgTxPackets;
+
+ @Field(index=20)
+ public long wifiBgRxPackets;
+
+ @Field(index=21)
+ public long wifiBgTxPackets;
+ }
+
+ @Line(tag="sgt", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class SignalStrengthTime extends Record {
+ @Field(index=0)
+ public long[] phoneSignalStrengthTimeMs;
+ }
+
+ @Line(tag="sst", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class SignalScanningTime extends Record {
+ @Field(index=0)
+ public long phoneSignalScanningTimeMs;
+ }
+
+ @Line(tag="uid", scope=Scope.UID, count=Count.MULTIPLE)
+ public static class Uid extends Record {
+ @Field(index=0)
+ public int uidKey;
+
+ @Field(index=1)
+ public String pkg;
+ }
+
+ @Line(tag="vers", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Version extends Record {
+ @Field(index=0)
+ public int dumpsysVersion;
+
+ @Field(index=1)
+ public int parcelVersion;
+
+ @Field(index=2)
+ public String startPlatformVersion;
+
+ @Field(index=3)
+ public String endPlatformVersion;
+ }
+
+ /**
+ * Codes for the warnings to classify warnings without parsing them.
+ */
+ public enum WarningId {
+ /**
+ * A row didn't have enough fields to match any records and let us extract
+ * a line type.
+ */
+ TOO_FEW_FIELDS_FOR_LINE_TYPE,
+
+ /**
+ * We couldn't find a Record for the given line type.
+ */
+ NO_MATCHING_LINE_TYPE,
+
+ /**
+ * Couldn't set the value of a field. Usually this is because the
+ * contents of a numeric type couldn't be parsed.
+ */
+ BAD_FIELD_TYPE,
+
+ /**
+ * There were extra field values in the input text.
+ */
+ TOO_MANY_FIELDS,
+
+ /**
+ * There were fields that we were expecting (for this version
+ * of the dumpsys) that weren't provided in the CSV data.
+ */
+ NOT_ENOUGH_FIELDS,
+
+ /**
+ * The dumpsys version in the 'vers' CSV line couldn't be parsed.
+ */
+ BAD_DUMPSYS_VERSION
+ }
+
+ /**
+ * A non-fatal problem detected during parsing.
+ */
+ public static class Warning {
+ private int mLineNumber;
+ private WarningId mId;
+ private ArrayList<String> mFields;
+ private String mMessage;
+ private String[] mExtras;
+
+ public Warning(int lineNumber, WarningId id, ArrayList<String> fields, String message,
+ String[] extras) {
+ mLineNumber = lineNumber;
+ mId = id;
+ mFields = fields;
+ mMessage = message;
+ mExtras = extras;
+ }
+
+ public int getLineNumber() {
+ return mLineNumber;
+ }
+
+ public ArrayList<String> getFields() {
+ return mFields;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ public String[] getExtras() {
+ return mExtras;
+ }
+ }
+
+ /**
+ * Base class for classes to set fields on Record objects via reflection.
+ */
+ private abstract static class FieldSetter {
+ private int mIndex;
+ private int mAdded;
+ protected java.lang.reflect.Field mField;
+
+ FieldSetter(int index, int added, java.lang.reflect.Field field) {
+ mIndex = index;
+ mAdded = added;
+ mField = field;
+ }
+
+ String getName() {
+ return mField.getName();
+ }
+
+ int getIndex() {
+ return mIndex;
+ }
+
+ int getAdded() {
+ return mAdded;
+ }
+
+ boolean isArray() {
+ return mField.getType().isArray();
+ }
+
+ abstract void setField(int lineNumber, Record object, String value) throws ParseException;
+ abstract void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException;
+
+ @Override
+ public String toString() {
+ final String className = getClass().getName();
+ int startIndex = Math.max(className.lastIndexOf('.'), className.lastIndexOf('$'));
+ if (startIndex < 0) {
+ startIndex = 0;
+ } else {
+ startIndex++;
+ }
+ return className.substring(startIndex) + "(index=" + mIndex + " added=" + mAdded
+ + " field=" + mField.getName()
+ + " type=" + mField.getType().getSimpleName()
+ + ")";
+ }
+ }
+
+ /**
+ * Sets int fields on Record objects using reflection.
+ */
+ private static class IntFieldSetter extends FieldSetter {
+ IntFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.setInt(object, Integer.parseInt(value.trim()));
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse as integer: " + value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final int[] array = new int[endIndex-startIndex];
+ for (int i=startIndex; i<endIndex; i++) {
+ final String value = values.get(startIndex+i);
+ try {
+ array[i] = Integer.parseInt(value.trim());
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse field "
+ + i + " as integer: " + value);
+ }
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets long fields on Record objects using reflection.
+ */
+ private static class LongFieldSetter extends FieldSetter {
+ LongFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.setLong(object, Long.parseLong(value.trim()));
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse as long: " + value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final long[] array = new long[endIndex-startIndex];
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ final String value = values.get(startIndex+i);
+ try {
+ array[i] = Long.parseLong(value.trim());
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse field "
+ + i + " as long: " + value);
+ }
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets String fields on Record objects using reflection.
+ */
+ private static class StringFieldSetter extends FieldSetter {
+ StringFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.set(object, value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final String[] array = new String[endIndex-startIndex];
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ array[i] = values.get(startIndex+1);
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets enum fields on Record objects using reflection.
+ *
+ * To be parsed automatically, enums must have a public final String tag
+ * field, which is the string that will appear in the csv for that enum value.
+ */
+ private static class EnumFieldSetter extends FieldSetter {
+ private final HashMap<String,Enum> mTags = new HashMap<String,Enum>();
+
+ EnumFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+
+ // Build the mapping of tags to values.
+ final Class<?> fieldType = field.getType();
+
+ java.lang.reflect.Field tagField = null;
+ try {
+ tagField = fieldType.getField("tag");
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException("Missing tag field."
+ + " To be parsed automatically, enums must have"
+ + " a String field called tag. Enum class: " + fieldType.getName()
+ + " Containing class: " + field.getDeclaringClass().getName()
+ + " Field: " + field.getName());
+
+ }
+ if (!String.class.equals(tagField.getType())) {
+ throw new RuntimeException("Tag field is not string."
+ + " To be parsed automatically, enums must have"
+ + " a String field called tag. Enum class: " + fieldType.getName()
+ + " Containing class: " + field.getDeclaringClass().getName()
+ + " Field: " + field.getName()
+ + " Tag field type: " + tagField.getType().getName());
+ }
+
+ for (final Object enumValue: fieldType.getEnumConstants()) {
+ String tag = null;
+ try {
+ tag = (String)tagField.get(enumValue);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ mTags.put(tag, (Enum)enumValue);
+ }
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ final Enum enumValue = mTags.get(value);
+ if (enumValue == null) {
+ throw new ParseException(lineNumber, "Could not find enum for field "
+ + getName() + " for tag: " + value);
+ }
+ try {
+ mField.set(object, enumValue);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final Object array = Array.newInstance(mField.getType().getComponentType(),
+ endIndex-startIndex);
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ final String value = values.get(startIndex+i);
+ final Enum enumValue = mTags.get(value);
+ if (enumValue == null) {
+ throw new ParseException(lineNumber, "Could not find enum for field "
+ + getName() + " for tag: " + value);
+ }
+ Array.set(array, i, enumValue);
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Factory for the record classes. Uses reflection to create
+ * the fields.
+ */
+ private static class RecordFactory {
+ private String mTag;
+ private Class<? extends Record> mSubclass;
+ private ArrayList<FieldSetter> mFieldSetters;
+
+ RecordFactory(String tag, Class<? extends Record> subclass,
+ ArrayList<FieldSetter> fieldSetters) {
+ mTag = tag;
+ mSubclass = subclass;
+ mFieldSetters = fieldSetters;
+ }
+
+ /**
+ * Create an object of one of the subclasses of Record, and fill
+ * in the fields marked with the Field annotation.
+ *
+ * @return a new Record with the fields filled in. If there are missing
+ * fields, the {@link Record.complete} field will be set to false.
+ */
+ Record create(RawBatteryStats bs, int dumpsysVersion, int lineNumber,
+ ArrayList<String> fieldValues) {
+ final boolean debug = false;
+ Record record = null;
+ try {
+ if (debug) {
+ System.err.println("Creating object: " + mSubclass.getSimpleName());
+ }
+ record = mSubclass.newInstance();
+ } catch (IllegalAccessException | InstantiationException
+ | ExceptionInInitializerError | SecurityException ex) {
+ throw new RuntimeException("Exception creating " + mSubclass.getName()
+ + " for '" + mTag + "' record.", ex);
+ }
+ record.complete = true;
+ int fieldIndex = 0;
+ int setterIndex = 0;
+ while (fieldIndex < fieldValues.size() && setterIndex < mFieldSetters.size()) {
+ final FieldSetter setter = mFieldSetters.get(setterIndex);
+
+ if (dumpsysVersion >= 0 && dumpsysVersion < setter.getAdded()) {
+ // The version being parsed doesn't have the field for this setter,
+ // so skip the setter but not the field.
+ setterIndex++;
+ continue;
+ }
+
+ final String value = fieldValues.get(fieldIndex);
+ try {
+ if (debug) {
+ System.err.println(" setting field " + setter + " to: " + value);
+ }
+ if (setter.isArray()) {
+ setter.setArray(lineNumber, record, fieldValues,
+ fieldIndex, fieldValues.size());
+ // The rest of the fields have been consumed.
+ fieldIndex = fieldValues.size();
+ setterIndex = mFieldSetters.size();
+ break;
+ } else {
+ setter.setField(lineNumber, record, value);
+ }
+ } catch (ParseException ex) {
+ bs.addWarning(lineNumber, WarningId.BAD_FIELD_TYPE, fieldValues,
+ ex.getMessage(), mTag, value);
+ record.complete = false;
+ }
+
+ fieldIndex++;
+ setterIndex++;
+ }
+
+ // If there are extra fields, this record is complete, there are just
+ // extra values, so we issue a warning but don't mark it incomplete.
+ if (fieldIndex < fieldValues.size()) {
+ bs.addWarning(lineNumber, WarningId.TOO_MANY_FIELDS, fieldValues,
+ "Line '" + mTag + "' has extra fields.",
+ mTag, Integer.toString(fieldIndex), Integer.toString(fieldValues.size()));
+ if (debug) {
+ for (int i=0; i<mFieldSetters.size(); i++) {
+ System.err.println(" setter: [" + i + "] " + mFieldSetters.get(i));
+ }
+ }
+ }
+
+ // If we have any fields that are missing, add a warning and return null.
+ for (; setterIndex < mFieldSetters.size(); setterIndex++) {
+ final FieldSetter setter = mFieldSetters.get(setterIndex);
+ if (dumpsysVersion >= 0 && dumpsysVersion >= setter.getAdded()) {
+ bs.addWarning(lineNumber, WarningId.NOT_ENOUGH_FIELDS, fieldValues,
+ "Line '" + mTag + "' missing field: index=" + setterIndex
+ + " name=" + setter.getName(),
+ mTag, Integer.toString(setterIndex));
+ record.complete = false;
+ }
+ }
+
+ return record;
+ }
+ }
+
+ /**
+ * Parse the input stream and return a RawBatteryStats object.
+ */
+ public static RawBatteryStats parse(InputStream input) throws ParseException, IOException {
+ final RawBatteryStats result = new RawBatteryStats();
+ result.parseImpl(input);
+ return result;
+ }
+
+ /**
+ * Get a record.
+ * <p>
+ * If multiple of that record are found, returns the first one. There will already
+ * have been a warning recorded if the count annotation did not match what was in the
+ * csv.
+ * <p>
+ * Returns null if there are no records of that type.
+ */
+ public <T extends Record> T getSingle(Class<T> cl) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return null;
+ }
+ // Notes:
+ // - List can never be empty because the list itself wouldn't have been added.
+ // - Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return (T)list.get(0);
+ }
+
+ /**
+ * Get a record.
+ * <p>
+ * If multiple of that record are found, returns the first one that matches that uid.
+ * <p>
+ * Returns null if there are no records of that type that match the given uid.
+ */
+ public <T extends Record> T getSingle(Class<T> cl, int uid) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return null;
+ }
+ for (final Record record: list) {
+ if (record.uid == uid) {
+ // Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return (T)record;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the records of the given type.
+ */
+ public <T extends Record> List<T> getMultiple(Class<T> cl) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return ImmutableList.<T>of();
+ }
+ // Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return ImmutableList.copyOf((List<T>)list);
+ }
+
+ /**
+ * Get the UIDs that are covered by this batterystats dump.
+ */
+ public Set<AttributionKey> getApps() {
+ return mApps;
+ }
+
+ /**
+ * No public constructor. Use {@link #parse}.
+ */
+ private RawBatteryStats() {
+ }
+
+ /**
+ * Get the list of Record objects that were parsed from the csv.
+ */
+ public List<Record> getRecords() {
+ return mRecords;
+ }
+
+ /**
+ * Gets the warnings that were encountered during parsing.
+ */
+ public List<Warning> getWarnings() {
+ return mWarnings;
+ }
+
+ /**
+ * Implementation of the csv parsing.
+ */
+ private void parseImpl(InputStream input) throws ParseException, IOException {
+ // Parse the csv
+ CsvParser.parse(input, new CsvParser.LineProcessor() {
+ @Override
+ public void onLine(int lineNumber, ArrayList<String> fields)
+ throws ParseException {
+ handleCsvLine(lineNumber, fields);
+ }
+ });
+
+ // Gather the records by class name for the getSingle() and getMultiple() functions.
+ indexRecords();
+
+ // Gather the uids from all the places UIDs come from, for getApps().
+ indexApps();
+ }
+
+ /**
+ * Handle a line of CSV input, creating the right Record object.
+ */
+ private void handleCsvLine(int lineNumber, ArrayList<String> fields) throws ParseException {
+ // The standard rows all have the 4 core fields. Anything less isn't what we're
+ // looking for.
+ if (fields.size() <= 4) {
+ addWarning(lineNumber, WarningId.TOO_FEW_FIELDS_FOR_LINE_TYPE, fields,
+ "Line with too few fields (" + fields.size() + ")",
+ Integer.toString(fields.size()));
+ return;
+ }
+
+ final String lineType = fields.get(3);
+
+ // Handle the vers line specially, because we need the version number
+ // to make the rest of the machinery work.
+ if ("vers".equals(lineType)) {
+ final String versionText = fields.get(4);
+ try {
+ mDumpsysVersion = Integer.parseInt(versionText);
+ } catch (NumberFormatException ex) {
+ addWarning(lineNumber, WarningId.BAD_DUMPSYS_VERSION, fields,
+ "Couldn't parse dumpsys version number: '" + versionText,
+ versionText);
+ }
+ }
+
+ // Find the right factory.
+ final RecordFactory factory = sFactories.get(lineType);
+ if (factory == null) {
+ addWarning(lineNumber, WarningId.NO_MATCHING_LINE_TYPE, fields,
+ "No Record for line type '" + lineType + "'",
+ lineType);
+ return;
+ }
+
+ // Create the record.
+ final Record record = factory.create(this, mDumpsysVersion, lineNumber, fields);
+ mRecords.add(record);
+ }
+
+ /**
+ * Add to the list of warnings.
+ */
+ private void addWarning(int lineNumber, WarningId id,
+ ArrayList<String> fields, String message, String... extras) {
+ mWarnings.add(new Warning(lineNumber, id, fields, message, extras));
+ final boolean debug = false;
+ if (debug) {
+ final StringBuilder text = new StringBuilder("line ");
+ text.append(lineNumber);
+ text.append(": WARNING: ");
+ text.append(message);
+ text.append("\n fields: ");
+ for (int i=0; i<fields.size(); i++) {
+ final String field = fields.get(i);
+ if (field.indexOf('"') >= 0) {
+ text.append('"');
+ text.append(field.replace("\"", "\"\""));
+ text.append('"');
+ } else {
+ text.append(field);
+ }
+ if (i != fields.size() - 1) {
+ text.append(',');
+ }
+ }
+ text.append('\n');
+ for (String extra: extras) {
+ text.append(" extra: ");
+ text.append(extra);
+ text.append('\n');
+ }
+ System.err.print(text.toString());
+ }
+ }
+
+ /**
+ * Group records by class name.
+ */
+ private void indexRecords() {
+ final HashMap<String,ArrayList<Record>> map = new HashMap<String,ArrayList<Record>>();
+
+ // Iterate over all of the records
+ for (Record record: mRecords) {
+ final String className = record.getClass().getName();
+
+ ArrayList<Record> list = map.get(className);
+ if (list == null) {
+ list = new ArrayList<Record>();
+ map.put(className, list);
+ }
+
+ list.add(record);
+ }
+
+ // Make it immutable
+ final HashMap<String,ImmutableList<Record>> result
+ = new HashMap<String,ImmutableList<Record>>();
+ for (HashMap.Entry<String,ArrayList<Record>> entry: map.entrySet()) {
+ result.put(entry.getKey(), ImmutableList.copyOf(entry.getValue()));
+ }
+
+ // Initialize here so uninitialized access will result in NPE.
+ mRecordsByType = ImmutableMap.copyOf(result);
+ }
+
+ /**
+ * Collect the UIDs from the csv.
+ *
+ * They come from two places.
+ * <ul>
+ * <li>The uid to package name map entries ({@link #Uid}) at the beginning.
+ * <li>The uid fields of the rest of the entries, some of which might not
+ * have package names associated with them.
+ * </ul>
+ *
+ * TODO: Is this where we should also do the logic about the special UIDs?
+ */
+ private void indexApps() {
+ final HashMap<Integer,HashSet<String>> uids = new HashMap<Integer,HashSet<String>>();
+
+ // The Uid rows, from which we get package names
+ for (Uid record: getMultiple(Uid.class)) {
+ HashSet<String> list = uids.get(record.uidKey);
+ if (list == null) {
+ list = new HashSet<String>();
+ uids.put(record.uidKey, list);
+ }
+ list.add(record.pkg);
+ }
+
+ // The uid fields of everything
+ for (Record record: mRecords) {
+ // The 0 in the INFO records isn't really root, it's just unfilled data.
+ // The root uid (0) will show up practically in every record, but don't force it.
+ if (record.category != Category.INFO) {
+ if (uids.get(record.uid) == null) {
+ // There is no other data about this UID, but it does exist!
+ uids.put(record.uid, new HashSet<String>());
+ }
+ }
+ }
+
+ // Turn our temporary lists of package names into AttributionKeys.
+ final HashSet<AttributionKey> result = new HashSet<AttributionKey>();
+ for (HashMap.Entry<Integer,HashSet<String>> entry: uids.entrySet()) {
+ result.add(new AttributionKey(entry.getKey(), entry.getValue()));
+ }
+
+ // Initialize here so uninitialized access will result in NPE.
+ mApps = ImmutableSet.copyOf(result);
+ }
+
+ /**
+ * Init the factory classes.
+ */
+ static {
+ for (Class<?> cl: RawBatteryStats.class.getClasses()) {
+ final Line lineAnnotation = cl.getAnnotation(Line.class);
+ if (lineAnnotation != null && Record.class.isAssignableFrom(cl)) {
+ final ArrayList<FieldSetter> fieldSetters = new ArrayList<FieldSetter>();
+
+ for (java.lang.reflect.Field field: cl.getFields()) {
+ final Field fa = field.getAnnotation(Field.class);
+ if (fa != null) {
+ final Class<?> fieldType = field.getType();
+ final Class<?> innerType = fieldType.isArray()
+ ? fieldType.getComponentType()
+ : fieldType;
+ if (Integer.TYPE.equals(innerType)) {
+ fieldSetters.add(new IntFieldSetter(fa.index(), fa.added(), field));
+ } else if (Long.TYPE.equals(innerType)) {
+ fieldSetters.add(new LongFieldSetter(fa.index(), fa.added(), field));
+ } else if (String.class.equals(innerType)) {
+ fieldSetters.add(new StringFieldSetter(fa.index(), fa.added(), field));
+ } else if (innerType.isEnum()) {
+ fieldSetters.add(new EnumFieldSetter(fa.index(), fa.added(), field));
+ } else {
+ throw new RuntimeException("Unsupported field type '"
+ + fieldType.getName() + "' on "
+ + cl.getName() + "." + field.getName());
+ }
+ }
+ }
+ // Sort by index
+ Collections.sort(fieldSetters, new Comparator<FieldSetter>() {
+ @Override
+ public int compare(FieldSetter a, FieldSetter b) {
+ return a.getIndex() - b.getIndex();
+ }
+ });
+ // Only the last one can be an array
+ for (int i=0; i<fieldSetters.size()-1; i++) {
+ if (fieldSetters.get(i).isArray()) {
+ throw new RuntimeException("Only the last (highest index) @Field"
+ + " in class " + cl.getName() + " can be an array: "
+ + fieldSetters.get(i).getName());
+ }
+ }
+ // Add to the map
+ sFactories.put(lineAnnotation.tag(), new RecordFactory(lineAnnotation.tag(),
+ (Class<Record>)cl, fieldSetters));
+ }
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/SpecialApp.java b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
new file mode 100644
index 0000000..df1e1fb
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/SpecialApp.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 com.android.powermodel;
+
+/**
+ * Identifiers for well-known apps that have unique characteristics.
+ *
+ * @more
+ * This includes three categories:
+ * <ul>
+ * <li><b>Built-in system components</b> – These have predefined UIDs that are
+ * always the same. For example, the system UID is always 1000.</li>
+ * <li><b>Well known apps with shared UIDs</b> – These do not have predefined
+ * UIDs (i.e. are different on each device), but since they have shared UIDs
+ * with varying sets of package names (GmsCore is the canonical example), we
+ * have special logic to capture these into a single entity with a well defined
+ * key. These have the {@link #uid uid} field set to
+ * {@link Uid#UID_VARIES Uid.UID_VARIES}.</li>
+ * <li><b>Synthetic remainder app</b> – The {@link #REMAINDER REMAINDER} app doesn't
+ * represent a real app. It contains accounting for usage which is not attributed
+ * to any UID. This app has the {@link #uid uid} field set to
+ * {@link Uid#UID_SYNTHETIC Uid.UID_SYNTHETIC}.</li>
+ * </ul>
+ */
+public enum SpecialApp {
+
+ /**
+ * Synthetic app that accounts for the remaining amount of resources used
+ * that is unaccounted for by apps, or overcounted because of inaccuracies
+ * in the model.
+ */
+ REMAINDER(Uid.UID_SYNTHETIC),
+
+ /**
+ * Synthetic app that holds system-wide numbers, for example the total amount
+ * of various resources used, device-wide.
+ */
+ GLOBAL(Uid.UID_SYNTHETIC),
+
+ SYSTEM(1000),
+
+ GOOGLE_SERVICES(Uid.UID_VARIES);
+
+ /**
+ * Constants for SpecialApps where the uid is not actually a UID.
+ */
+ public static class Uid {
+ /**
+ * Constant to indicate that this special app does not have a fixed UID.
+ */
+ public static final int UID_VARIES = -1;
+
+ /**
+ * Constant to indicate that this special app is not actually an app with a UID.
+ *
+ * @see SpecialApp#REMAINDER
+ * @see SpecialApp#GLOBAL
+ */
+ public static final int UID_SYNTHETIC = -2;
+ }
+
+ /**
+ * The fixed UID value of this special app, or {@link #UID_VARIES} if there
+ * isn't one.
+ */
+ public final int uid;
+
+ private SpecialApp(int uid) {
+ this.uid = uid;
+ }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
index 27d25b8..63ff3a6 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class AudioProfile extends ComponentProfile {
+ public float onMa;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
index 27d25b8..8f5e7d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class BluetoothProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
index 27d25b8..8ee22d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CameraProfile extends ComponentProfile {
+ public float onMa;
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
new file mode 100644
index 0000000..0b34fc8
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CpuProfile extends ComponentProfile {
+ public float suspendMa;
+ public float idleMa;
+ public float activeMa;
+ public Cluster[] clusters;
+
+ public static class Cluster {
+ public int coreCount;
+ public float onMa;
+ public Frequency[] frequencies;
+ }
+
+ public static class Frequency {
+ public int speedHz;
+ public float onMa;
+ }
+
+ public static class Builder {
+ private float mSuspendMa;
+ private float mIdleMa;
+ private float mActiveMa;
+ private int[] mCoreCount;
+ private HashMap<Integer,Float> mClusterOnPower = new HashMap<Integer,Float>();
+ private HashMap<Integer,int[]> mCoreSpeeds = new HashMap<Integer,int[]>();
+ private HashMap<Integer,float[]> mCorePower = new HashMap<Integer,float[]>();
+
+ public Builder() {
+ }
+
+ public void setSuspendMa(float value) throws ParseException {
+ mSuspendMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setActiveMa(float value) throws ParseException {
+ mActiveMa = value;
+ }
+
+ public void setCoreCount(int[] value) throws ParseException {
+ mCoreCount = Arrays.copyOf(value, value.length);
+ }
+
+ public void setClusterPower(int cluster, float value) throws ParseException {
+ mClusterOnPower.put(cluster, value);
+ }
+
+ public void setCoreSpeeds(int cluster, int[] value) throws ParseException {
+ mCoreSpeeds.put(cluster, Arrays.copyOf(value, value.length));
+ float[] power = mCorePower.get(cluster);
+ if (power != null && value.length != power.length) {
+ throw new ParseException("length of cpu.core_speeds.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.core_power.cluster" + cluster + " (" + power.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_speeds.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public void setCorePower(int cluster, float[] value) throws ParseException {
+ mCorePower.put(cluster, Arrays.copyOf(value, value.length));
+ int[] speeds = mCoreSpeeds.get(cluster);
+ if (speeds != null && value.length != speeds.length) {
+ throw new ParseException("length of cpu.core_power.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.clusters.cores" + cluster + " (" + speeds.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_power.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public CpuProfile build() throws ParseException {
+ final CpuProfile result = new CpuProfile();
+
+ // Validate cluster count
+
+ // All null or none null
+ // TODO
+
+ // Same size
+ // TODO
+
+ // No gaps
+ // TODO
+
+ // Fill in values
+ result.suspendMa = mSuspendMa;
+ result.idleMa = mIdleMa;
+ result.activeMa = mActiveMa;
+ if (mCoreCount != null) {
+ result.clusters = new Cluster[mCoreCount.length];
+ for (int i = 0; i < result.clusters.length; i++) {
+ final Cluster cluster = result.clusters[i] = new Cluster();
+ cluster.coreCount = mCoreCount[i];
+ cluster.onMa = mClusterOnPower.get(i);
+ int[] speeds = mCoreSpeeds.get(i);
+ float[] power = mCorePower.get(i);
+ cluster.frequencies = new Frequency[speeds.length];
+ for (int j = 0; j < speeds.length; j++) {
+ final Frequency freq = cluster.frequencies[j] = new Frequency();
+ freq.speedHz = speeds[j];
+ freq.onMa = power[j];
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
similarity index 70%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
index 27d25b8..c85f3ff 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class FlashlightProfile extends ComponentProfile {
+ public float onMa;
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
new file mode 100644
index 0000000..83c06a7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class GpsProfile extends ComponentProfile {
+ public float onMa;
+ public float[] signalQualityMa;
+
+ public static class Builder {
+ private float onMa;
+ private float[] mSignalQualityMa;
+
+ public Builder() {
+ }
+
+ public void setOnMa(float value) throws ParseException {
+ onMa = value;
+ }
+
+ public void setSignalMa(float[] value) throws ParseException {
+ mSignalQualityMa = value;
+ }
+
+ public GpsProfile build() throws ParseException {
+ GpsProfile result = new GpsProfile();
+ result.onMa = onMa;
+ result.signalQualityMa = mSignalQualityMa == null
+ ? new float[0]
+ : Arrays.copyOf(mSignalQualityMa, mSignalQualityMa.length);
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
new file mode 100644
index 0000000..cb70051
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.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 com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the celluar modem on behalf of an app.
+ */
+public class ModemAppActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemAppActivity.
+ */
+ public ModemAppActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * The number of packets received by the app.
+ */
+ public long rxPacketCount;
+
+ /**
+ * The number of packets sent by the app.
+ */
+ public long txPacketCount;
+
+ @Override
+ public ModemAppPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ // Profile
+ final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+ if (modemProfile == null) {
+ // TODO: This is kind of a big problem... Should this throw instead?
+ return null;
+ }
+
+ // Activity
+ final ModemGlobalActivity global
+ = (ModemGlobalActivity)activityReport.findGlobalComponent(Component.MODEM);
+ if (global == null) {
+ return null;
+ }
+
+ final double averageModemPowerMa = getAverageModemPowerMa(modemProfile);
+ final long totalPacketCount = global.rxPacketCount + global.txPacketCount;
+ final long appPacketCount = this.rxPacketCount + this.txPacketCount;
+
+ final ModemAppPower result = new ModemAppPower();
+ result.attribution = this.attribution;
+ result.activity = this;
+ result.powerMah = Conversion.msToHr(
+ (totalPacketCount > 0 ? (appPacketCount / (double)totalPacketCount) : 0)
+ * global.totalActiveTimeMs
+ * averageModemPowerMa);
+ return result;
+ }
+
+ static final double getAverageModemPowerMa(ModemProfile profile) {
+ double sumMa = profile.getRxMa();
+ for (float powerAtTxLevelMa: profile.getTxMa()) {
+ sumMa += powerAtTxLevelMa;
+ }
+ return sumMa / (profile.getTxMa().length + 1);
+ }
+}
+
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
similarity index 74%
copy from core/java/android/view/intelligence/ContentCaptureEvent.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
index c66a6cb..f553127 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.view.intelligence;
+package com.android.powermodel.component;
-parcelable ContentCaptureEvent;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemAppPower extends ComponentPower<ModemAppActivity> {
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
new file mode 100644
index 0000000..6dbfbc2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel.component;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.RawBatteryStats;
+import com.android.powermodel.SpecialApp;
+
+public class ModemBatteryStatsReader {
+ private ModemBatteryStatsReader() {
+ }
+
+ public static List<ComponentActivity> createActivities(RawBatteryStats bs) {
+ final List<ComponentActivity> result = new ArrayList<ComponentActivity>();
+
+ // The whole system
+ createGlobal(result, bs);
+
+ // The apps
+ createApps(result, bs);
+
+ // The synthetic "cell" app.
+ createRemainder(result, bs);
+
+ return result;
+ }
+
+ private static void createGlobal(List<ComponentActivity> result, RawBatteryStats bs) {
+ final ModemGlobalActivity global
+ = new ModemGlobalActivity(new AttributionKey(SpecialApp.GLOBAL));
+
+ final RawBatteryStats.GlobalNetwork gn = bs.getSingle(RawBatteryStats.GlobalNetwork.class);
+ final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+ // Null here just means no network activity.
+ if (gn != null && misc != null) {
+ global.rxPacketCount = gn.mobileRxTotalPackets;
+ global.txPacketCount = gn.mobileTxTotalPackets;
+
+ global.totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+ }
+
+ result.add(global);
+ }
+
+ private static void createApps(List<ComponentActivity> result, RawBatteryStats bs) {
+ for (AttributionKey key: bs.getApps()) {
+ final int uid = key.getUid();
+ final RawBatteryStats.Network network
+ = bs.getSingle(RawBatteryStats.Network.class, uid);
+
+ // Null here just means no network activity.
+ if (network != null) {
+ final ModemAppActivity app = new ModemAppActivity(key);
+
+ app.rxPacketCount = network.mobileRxPackets;
+ app.txPacketCount = network.mobileTxPackets;
+
+ result.add(app);
+ }
+ }
+ }
+
+ private static void createRemainder(List<ComponentActivity> result, RawBatteryStats bs) {
+ final RawBatteryStats.SignalStrengthTime strength
+ = bs.getSingle(RawBatteryStats.SignalStrengthTime.class);
+ final RawBatteryStats.SignalScanningTime scanning
+ = bs.getSingle(RawBatteryStats.SignalScanningTime.class);
+ final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+ if (strength != null && scanning != null && misc != null) {
+ final ModemRemainderActivity remainder
+ = new ModemRemainderActivity(new AttributionKey(SpecialApp.REMAINDER));
+
+ // Signal strength buckets
+ remainder.strengthTimeMs = strength.phoneSignalStrengthTimeMs;
+
+ // Time spent scanning
+ remainder.scanningTimeMs = scanning.phoneSignalScanningTimeMs;
+
+ // Unaccounted for active time
+ final long totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+ long appActiveTimeMs = 0;
+ for (RawBatteryStats.Network nw: bs.getMultiple(RawBatteryStats.Network.class)) {
+ appActiveTimeMs += nw.mobileRadioActiveTimeUs / 1000;
+ }
+ remainder.activeTimeMs = totalActiveTimeMs - appActiveTimeMs;
+
+ result.add(remainder);
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
new file mode 100644
index 0000000..a53b53e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.ComponentPower;
+import com.android.powermodel.PowerProfile;
+
+/**
+ * Encapsulates total work done by the modem for the device.
+ */
+public class ModemGlobalActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemGlobalActivity.
+ */
+ public ModemGlobalActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * Returns the total number of packets received in the whole device.
+ */
+ public long rxPacketCount;
+
+ /**
+ * Returns the total number of packets sent in the whole device.
+ */
+ public long txPacketCount;
+
+ /**
+ * Returns the total time the radio was active in the whole device.
+ */
+ public long totalActiveTimeMs;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
new file mode 100644
index 0000000..cda72ee
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ModemProfile extends ComponentProfile {
+ public float sleepMa;
+ public float idleMa;
+ public float scanningMa;
+ public float rxMa;
+ public float[] txMa;
+
+ public float getSleepMa() {
+ return sleepMa;
+ }
+
+ public float getIdleMa() {
+ return idleMa;
+ }
+
+ public float getRxMa() {
+ return rxMa;
+ }
+
+ public float[] getTxMa() {
+ return Arrays.copyOf(txMa, txMa.length);
+ }
+
+ public float getScanningMa() {
+ return scanningMa;
+ }
+
+ public static class Builder {
+ private float mSleepMa;
+ private float mIdleMa;
+ private float mRxMa;
+ private float[] mTxMa;
+ private float mScanningMa;
+
+ public Builder() {
+ }
+
+ public void setSleepMa(float value) throws ParseException {
+ mSleepMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setRxMa(float value) throws ParseException {
+ mRxMa = value;
+ }
+
+ public void setTxMa(float[] value) throws ParseException {
+ mTxMa = Arrays.copyOf(value, value.length);
+ }
+
+ public void setScanningMa(float value) throws ParseException {
+ mScanningMa = value;
+ }
+
+ public ModemProfile build() throws ParseException {
+ ModemProfile result = new ModemProfile();
+ result.sleepMa = mSleepMa;
+ result.idleMa = mIdleMa;
+ result.rxMa = mRxMa;
+ result.txMa = mTxMa == null ? new float[0] : mTxMa;
+ result.scanningMa = mScanningMa;
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
new file mode 100644
index 0000000..0e268c2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.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.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the remaining
+ */
+public class ModemRemainderActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemRemainderActivity.
+ */
+ public ModemRemainderActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * Number of milliseconds spent at each of the signal strengths.
+ */
+ public long[] strengthTimeMs;
+
+ /**
+ * Number of milliseconds spent scanning for a network.
+ */
+ public long scanningTimeMs;
+
+ /**
+ * Number of milliseconds that the radio is active for reasons other
+ * than an app transmitting and receiving data.
+ */
+ public long activeTimeMs;
+
+ @Override
+ public ModemRemainderPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ // Profile
+ final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+ if (modemProfile == null) {
+ return null;
+ }
+
+ // Activity
+ final ModemRemainderPower result = new ModemRemainderPower();
+ result.attribution = this.attribution;
+ result.activity = this;
+
+ // strengthMah
+ // TODO: If the array lengths don't match... then?
+ result.strengthMah = new double[this.strengthTimeMs.length];
+ for (int i=0; i<this.strengthTimeMs.length; i++) {
+ result.strengthMah[i] = Conversion.msToHr(
+ this.strengthTimeMs[i] * modemProfile.getTxMa()[i]);
+ result.powerMah += result.strengthMah[i];
+ }
+
+ // scanningMah
+ result.scanningMah = Conversion.msToHr(this.scanningTimeMs * modemProfile.getScanningMa());
+ result.powerMah += result.scanningMah;
+
+ // activeMah
+ result.activeMah = Conversion.msToHr(
+ this.activeTimeMs * ModemAppActivity.getAverageModemPowerMa(modemProfile));
+ result.powerMah += result.activeMah;
+
+ return result;
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
index 27d25b8..7f38cd3 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
@@ -13,12 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemRemainderPower extends ComponentPower<ModemRemainderActivity> {
+
+ public double[] strengthMah;
+
+ public double scanningMah;
+
+ public double activeMah;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
index 27d25b8..e1051c6 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ScreenProfile extends ComponentProfile {
+ public float onMa;
+ public float fullMa;
+ public float ambientMa;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
index 27d25b8..5152795 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class VideoProfile extends ComponentProfile {
+ public float onMa;
}
+
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
index 27d25b8..6f424bf 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class WifiProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/util/Conversion.java b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
new file mode 100644
index 0000000..e556c25
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.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.powermodel.util;
+
+public class Conversion {
+
+ /**
+ * Convert the the float[] to an int[].
+ * <p>
+ * Values are rounded to the nearest integral value. Null input
+ * results in null output.
+ */
+ public static int[] toIntArray(float[] value) {
+ if (value == null) {
+ return null;
+ }
+ int[] result = new int[value.length];
+ for (int i=0; i<result.length; i++) {
+ result[i] = (int)(value[i] + 0.5f);
+ }
+ return result;
+ }
+
+ public static double msToHr(double ms) {
+ return ms / 3600.0 / 1000.0;
+ }
+
+ /**
+ * No public constructor.
+ */
+ private Conversion() {
+ }
+}
diff --git a/tools/powermodel/test-resource/bs.csv b/tools/powermodel/test-resource/bs.csv
new file mode 100644
index 0000000..6e84120
--- /dev/null
+++ b/tools/powermodel/test-resource/bs.csv
@@ -0,0 +1,7 @@
+9,0,i,vers,32,177,PPR1.180326.002,PQ1A.181105.015
+9,0,i,uid,10139,com.google.android.gm
+9,0,l,gn,108060756,17293456,4896592,3290614,97840,72941,6903,8107,390,105
+9,0,l,m,2590630,0,384554,3943868,5113727,265,2565483,0,16,0,0,0,0,192,25331,3472068,17,3543323,14,614050,0
+9,10139,l,nt,13688501,534571,13842,7792,9925,5577,30,67,190051799,27,0,0,5,3,126020,42343,13842,7792,207,167,30,67
+9,0,l,sgt,3066958,0,34678,1643364,7045084
+9,0,l,sst,2443805
diff --git a/tools/powermodel/test-resource/power_profile.xml b/tools/powermodel/test-resource/power_profile.xml
new file mode 100644
index 0000000..8e388ea
--- /dev/null
+++ b/tools/powermodel/test-resource/power_profile.xml
@@ -0,0 +1,170 @@
+<?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.
+-->
+
+<!-- Test power profile that parses correctly. -->
+<device>
+ <item name="battery.capacity">2915</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value>
+ <value>2</value>
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">1.3</item>
+
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">3.9</item>
+
+ <!-- Additional power consumption by CPU excluding cluster and core when
+ running -->
+ <item name="cpu.active">18.33</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running
+ excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.41</item>
+
+ <!-- Additional power consumption by CPU cluster1 itself when running
+ excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">5.29</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>100000</value>
+ <value>303200</value>
+ <value>380000</value>
+ <value>476000</value>
+ <value>552800</value>
+ <value>648800</value>
+ <value>725600</value>
+ <value>802400</value>
+ <value>879200</value>
+ </array>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>825600</value>
+ <value>902400</value>
+ <value>979200</value>
+ <value>1056000</value>
+ <value>1209600</value>
+ <value>1286400</value>
+ <value>1363200</value>
+ </array>
+
+ <!-- Additional power used by a CPU core from cluster 0 when running at
+ different speeds, excluding cluster and active cost -->
+ <array name="cpu.core_power.cluster0">
+ <value>0.29</value>
+ <value>0.63</value>
+ <value>1.23</value>
+ <value>1.24</value>
+ <value>2.47</value>
+ <value>2.54</value>
+ <value>3.60</value>
+ <value>3.64</value>
+ <value>4.42</value>
+ </array>
+
+ <!-- Additional power used by a CPU core from cluster 1 when running at
+ different speeds, excluding cluster and active cost -->
+ <array name="cpu.core_power.cluster1">
+ <value>28.98</value>
+ <value>31.40</value>
+ <value>33.33</value>
+ <value>40.12</value>
+ <value>44.10</value>
+ <value>90.14</value>
+ <value>100</value>
+ </array>
+
+ <!-- Additional power used when screen is ambient mode -->
+ <item name="ambient.on">12</item>
+
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">102.4</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">1234</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">1233.47</item>
+
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">941</item>
+
+ <!-- Additional power used when video is playing -->
+ <item name="video">123</item>
+
+ <!-- Additional power used when audio is playing -->
+ <item name="audio">12</item>
+
+ <!-- Cellular modem related values.-->
+ <item name="modem.controller.sleep">1</item>
+ <item name="modem.controller.idle">44</item>
+ <item name="modem.controller.rx">11</item>
+ <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+ <value>16</value>
+ <value>19</value>
+ <value>22</value>
+ <value>73</value>
+ <value>132</value>
+ </array>
+ <item name="modem.controller.voltage">1400</item>
+ <item name="radio.scanning">12</item>
+
+ <!-- GPS related values.-->
+ <item name="gps.on">1</item>
+ <array name="gps.signalqualitybased"> <!-- Strength 0 to 1 -->
+ <value>88</value>
+ <value>07</value>
+ </array>
+ <item name="gps.voltage">1500</item>
+
+ <!-- Idle Receive current for wifi radio in mA.-->
+ <item name="wifi.controller.idle">2</item>
+
+ <!-- Rx current for wifi radio in mA.-->
+ <item name="wifi.controller.rx">123</item>
+
+ <!-- Tx current for wifi radio in mA-->
+ <item name="wifi.controller.tx">333</item>
+
+ <!-- Operating volatage for wifi radio in mV.-->
+ <item name="wifi.controller.voltage">3700</item>
+
+ <!-- Idle current for bluetooth in mA.-->
+ <item name="bluetooth.controller.idle">0.02</item>
+
+ <!-- Rx current for bluetooth in mA.-->
+ <item name="bluetooth.controller.rx">3</item>
+
+ <!-- Tx current for bluetooth in mA-->
+ <item name="bluetooth.controller.tx">5</item>
+
+ <!-- Operating voltage for bluetooth in mV.-->
+ <item name="bluetooth.controller.voltage">3300</item>
+
+</device>
+
+
diff --git a/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
new file mode 100644
index 0000000..e7b2c37
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppActivity;
+import com.android.powermodel.component.ModemGlobalActivity;
+import com.android.powermodel.component.ModemRemainderActivity;
+
+/**
+ * Tests {@link BatteryStatsReader}.
+ */
+public class BatteryStatsReaderTest {
+ private static InputStream loadCsvStream() {
+ return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+ }
+
+ @Test public void testModemGlobal() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final AppActivity global = report.findApp(SpecialApp.GLOBAL);
+ Assert.assertNotNull(global);
+
+ final ModemGlobalActivity modem
+ = (ModemGlobalActivity)global.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(97840, modem.rxPacketCount);
+ Assert.assertEquals(72941, modem.txPacketCount);
+ Assert.assertEquals(5113727, modem.totalActiveTimeMs);
+ }
+
+ @Test public void testModemApp() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final List<AppActivity> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppActivity gmail = gmailList.get(0);
+
+ final ModemAppActivity modem
+ = (ModemAppActivity)gmail.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(9925, modem.rxPacketCount);
+ Assert.assertEquals(5577, modem.txPacketCount);
+ }
+
+ @Test public void testModemRemainder() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final AppActivity remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ final ModemRemainderActivity modem
+ = (ModemRemainderActivity)remainder.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertArrayEquals(new long[] { 3066958, 0, 34678, 1643364, 7045084 },
+ modem.strengthTimeMs);
+ Assert.assertEquals(2443805, modem.scanningTimeMs);
+ Assert.assertEquals(4923676, modem.activeTimeMs);
+ }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/CsvParserTest.java b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
new file mode 100644
index 0000000..55dde41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class CsvParserTest {
+
+ class LineCollector implements CsvParser.LineProcessor {
+ ArrayList<ArrayList<String>> results = new ArrayList<ArrayList<String>>();
+
+ @Override
+ public void onLine(int lineNumber, ArrayList<String> fields) {
+ System.out.println(lineNumber);
+ for (String str: fields) {
+ System.out.println("-->" + str + "<--");
+ }
+ results.add(fields);
+ }
+ }
+
+ private void assertEquals(String[][] expected, ArrayList<ArrayList<String>> results) {
+ final String[][] resultArray = new String[results.size()][];
+ for (int i=0; i<results.size(); i++) {
+ final ArrayList<String> list = results.get(i);
+ resultArray[i] = list.toArray(new String[list.size()]);
+ }
+ Assert.assertArrayEquals(expected, resultArray);
+ }
+
+ private String makeString(int length) {
+ final StringBuilder str = new StringBuilder();
+ for (int i=0; i<length; i++) {
+ str.append('a');
+ }
+ return str.toString();
+ }
+
+ @Test public void testEmpty() throws Exception {
+ final String text = "";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ }, collector.results);
+ }
+
+ @Test public void testOnlyNewline() throws Exception {
+ final String text = "\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ }, collector.results);
+ }
+
+ @Test public void testTwoLines() throws Exception {
+ final String text = "one,twoo,3\nfour,5,six\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "one", "twoo", "3", },
+ { "four", "5", "six", },
+ }, collector.results);
+ }
+
+
+ @Test public void testEscapedEmpty() throws Exception {
+ final String text = "\"\",\"\",\"\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "", "", "", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedText() throws Exception {
+ final String text = "\"one\",\"twoo\",\"3\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "one", "twoo", "3", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedQuotes() throws Exception {
+ final String text = "\"\"\"\",\"\"\"\"\"\",\"\"\"\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "\"", "\"\"", "\"", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedCommas() throws Exception {
+ final String text = "\",\",\",\",\",\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { ",", ",", ",", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedQuotesAndCommas() throws Exception {
+ final String text = "\"\"\",\",\"\"\",\",\"\"\",\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "\",", "\",", "\",", },
+ }, collector.results);
+ }
+
+ @Test public void testNoNewline() throws Exception {
+ final String text = "a,b,c";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "c", }
+ }, collector.results);
+ }
+
+ @Test public void testNoNewlineWithCommas() throws Exception {
+ final String text = "a,b,,";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "", "" }
+ }, collector.results);
+ }
+
+ @Test public void testNoNewlineWithQuote() throws Exception {
+ final String text = "a,b,\",\"";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "," }
+ }, collector.results);
+ }
+
+ @Test public void testNoCommas() throws Exception {
+ final String text = "aasdfadfadfad";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "aasdfadfadfad", }
+ }, collector.results);
+ }
+
+ @Test public void testMaxLength() throws Exception {
+ final String text = makeString(CsvParser.MAX_FIELD_SIZE);
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { text, }
+ }, collector.results);
+ }
+
+ @Test public void testMaxLengthTwice() throws Exception {
+ String big = makeString(CsvParser.MAX_FIELD_SIZE);
+ final String text = big + "," + big;
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, big, }
+ }, collector.results);
+ }
+
+ @Test public void testTooLong() throws Exception {
+ final String text = makeString(CsvParser.MAX_FIELD_SIZE+1);
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ try {
+ CsvParser.parse(is, collector);
+ throw new RuntimeException("Expected CsvParser.parse to throw ParseException");
+ } catch (ParseException ex) {
+ // good
+ }
+ }
+
+ @Test public void testBufferBoundary() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",b,c,d,e,f,g";
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "b", "c", "d", "e", "f", "g", }
+ }, collector.results);
+ }
+
+ @Test public void testBufferBoundaryEmpty() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",,,,,,";
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "", "", "", "", "", "", }
+ }, collector.results);
+ }
+
+ // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+ @Test public void testBufferBoundaryEscapingEven() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-2);
+ final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "\"\"\"\"\"", big }
+ }, collector.results);
+ }
+
+ // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+ @Test public void testBufferBoundaryEscapingOdd() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "\"\"\"\"\"", big }
+ }, collector.results);
+ }
+
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
new file mode 100644
index 0000000..ab45831
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.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.powermodel;
+
+import java.io.InputStream;
+
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import org.junit.Assert;
+import org.junit.Test;
+
+/*
+ * Additional tests needed:
+ * - CPU clusters with mismatching counts of speeds and coefficients
+ * - Extra fields
+ * - Name listed twice
+ */
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class PowerProfileTest {
+ private static final float EPSILON = 0.00001f;
+
+ private static InputStream loadPowerProfileStream() {
+ return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+ }
+
+ @Test public void testReadGood() throws Exception {
+ final InputStream is = loadPowerProfileStream();
+
+ final PowerProfile profile = PowerProfile.parse(is);
+
+ // Audio
+ final AudioProfile audio = (AudioProfile)profile.getComponent(Component.AUDIO);
+ Assert.assertEquals(12.0f, audio.onMa, EPSILON);
+
+ // Bluetooth
+ final BluetoothProfile bluetooth
+ = (BluetoothProfile)profile.getComponent(Component.BLUETOOTH);
+ Assert.assertEquals(0.02f, bluetooth.idleMa, EPSILON);
+ Assert.assertEquals(3.0f, bluetooth.rxMa, EPSILON);
+ Assert.assertEquals(5.0f, bluetooth.txMa, EPSILON);
+
+ // Camera
+ final CameraProfile camera = (CameraProfile)profile.getComponent(Component.CAMERA);
+ Assert.assertEquals(941.0f, camera.onMa, EPSILON);
+
+ // CPU
+ final CpuProfile cpu = (CpuProfile)profile.getComponent(Component.CPU);
+ Assert.assertEquals(1.3f, cpu.suspendMa, EPSILON);
+ Assert.assertEquals(3.9f, cpu.idleMa, EPSILON);
+ Assert.assertEquals(18.33f, cpu.activeMa, EPSILON);
+ Assert.assertEquals(2, cpu.clusters.length);
+ // Cluster 0
+ Assert.assertEquals(4, cpu.clusters[0].coreCount);
+ Assert.assertEquals(2.41f, cpu.clusters[0].onMa, EPSILON);
+ Assert.assertEquals(9, cpu.clusters[0].frequencies.length, EPSILON);
+ Assert.assertEquals(100000, cpu.clusters[0].frequencies[0].speedHz);
+ Assert.assertEquals(0.29f, cpu.clusters[0].frequencies[0].onMa, EPSILON);
+ Assert.assertEquals(303200, cpu.clusters[0].frequencies[1].speedHz);
+ Assert.assertEquals(0.63f, cpu.clusters[0].frequencies[1].onMa, EPSILON);
+ Assert.assertEquals(380000, cpu.clusters[0].frequencies[2].speedHz);
+ Assert.assertEquals(1.23f, cpu.clusters[0].frequencies[2].onMa, EPSILON);
+ Assert.assertEquals(476000, cpu.clusters[0].frequencies[3].speedHz);
+ Assert.assertEquals(1.24f, cpu.clusters[0].frequencies[3].onMa, EPSILON);
+ Assert.assertEquals(552800, cpu.clusters[0].frequencies[4].speedHz);
+ Assert.assertEquals(2.47f, cpu.clusters[0].frequencies[4].onMa, EPSILON);
+ Assert.assertEquals(648800, cpu.clusters[0].frequencies[5].speedHz);
+ Assert.assertEquals(2.54f, cpu.clusters[0].frequencies[5].onMa, EPSILON);
+ Assert.assertEquals(725600, cpu.clusters[0].frequencies[6].speedHz);
+ Assert.assertEquals(3.60f, cpu.clusters[0].frequencies[6].onMa, EPSILON);
+ Assert.assertEquals(802400, cpu.clusters[0].frequencies[7].speedHz);
+ Assert.assertEquals(3.64f, cpu.clusters[0].frequencies[7].onMa, EPSILON);
+ Assert.assertEquals(879200, cpu.clusters[0].frequencies[8].speedHz);
+ Assert.assertEquals(4.42f, cpu.clusters[0].frequencies[8].onMa, EPSILON);
+ // Cluster 1
+ Assert.assertEquals(2, cpu.clusters[1].coreCount);
+ Assert.assertEquals(5.29f, cpu.clusters[1].onMa, EPSILON);
+ Assert.assertEquals(7, cpu.clusters[1].frequencies.length, EPSILON);
+ Assert.assertEquals(825600, cpu.clusters[1].frequencies[0].speedHz);
+ Assert.assertEquals(28.98f, cpu.clusters[1].frequencies[0].onMa, EPSILON);
+ Assert.assertEquals(902400, cpu.clusters[1].frequencies[1].speedHz);
+ Assert.assertEquals(31.40f, cpu.clusters[1].frequencies[1].onMa, EPSILON);
+ Assert.assertEquals(979200, cpu.clusters[1].frequencies[2].speedHz);
+ Assert.assertEquals(33.33f, cpu.clusters[1].frequencies[2].onMa, EPSILON);
+ Assert.assertEquals(1056000, cpu.clusters[1].frequencies[3].speedHz);
+ Assert.assertEquals(40.12f, cpu.clusters[1].frequencies[3].onMa, EPSILON);
+ Assert.assertEquals(1209600, cpu.clusters[1].frequencies[4].speedHz);
+ Assert.assertEquals(44.10f, cpu.clusters[1].frequencies[4].onMa, EPSILON);
+ Assert.assertEquals(1286400, cpu.clusters[1].frequencies[5].speedHz);
+ Assert.assertEquals(90.14f, cpu.clusters[1].frequencies[5].onMa, EPSILON);
+ Assert.assertEquals(1363200, cpu.clusters[1].frequencies[6].speedHz);
+ Assert.assertEquals(100f, cpu.clusters[1].frequencies[6].onMa, EPSILON);
+
+ // Flashlight
+ final FlashlightProfile flashlight
+ = (FlashlightProfile)profile.getComponent(Component.FLASHLIGHT);
+ Assert.assertEquals(1233.47f, flashlight.onMa, EPSILON);
+
+ // GPS
+ final GpsProfile gps = (GpsProfile)profile.getComponent(Component.GPS);
+ Assert.assertEquals(1.0f, gps.onMa, EPSILON);
+ Assert.assertEquals(2, gps.signalQualityMa.length);
+ Assert.assertEquals(88.0f, gps.signalQualityMa[0], EPSILON);
+ Assert.assertEquals(7.0f, gps.signalQualityMa[1], EPSILON);
+
+ // Modem
+ final ModemProfile modem = (ModemProfile)profile.getComponent(Component.MODEM);
+ Assert.assertEquals(1.0f, modem.sleepMa, EPSILON);
+ Assert.assertEquals(44.0f, modem.idleMa, EPSILON);
+ Assert.assertEquals(12.0f, modem.scanningMa, EPSILON);
+ Assert.assertEquals(11.0f, modem.rxMa, EPSILON);
+ Assert.assertEquals(5, modem.txMa.length);
+ Assert.assertEquals(16.0f, modem.txMa[0], EPSILON);
+ Assert.assertEquals(19.0f, modem.txMa[1], EPSILON);
+ Assert.assertEquals(22.0f, modem.txMa[2], EPSILON);
+ Assert.assertEquals(73.0f, modem.txMa[3], EPSILON);
+ Assert.assertEquals(132.0f, modem.txMa[4], EPSILON);
+
+ // Screen
+ final ScreenProfile screen = (ScreenProfile)profile.getComponent(Component.SCREEN);
+ Assert.assertEquals(102.4f, screen.onMa, EPSILON);
+ Assert.assertEquals(1234.0f, screen.fullMa, EPSILON);
+ Assert.assertEquals(12.0f, screen.ambientMa, EPSILON);
+
+ // Video
+ final VideoProfile video = (VideoProfile)profile.getComponent(Component.VIDEO);
+ Assert.assertEquals(123.0f, video.onMa, EPSILON);
+
+ // Wifi
+ final WifiProfile wifi = (WifiProfile)profile.getComponent(Component.WIFI);
+ Assert.assertEquals(2.0f, wifi.idleMa, EPSILON);
+ Assert.assertEquals(123.0f, wifi.rxMa, EPSILON);
+ Assert.assertEquals(333.0f, wifi.txMa, EPSILON);
+ }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerReportTest.java b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
new file mode 100644
index 0000000..1a61737
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppPower;
+import com.android.powermodel.component.ModemRemainderPower;
+
+/**
+ * Tests {@link PowerReport}.
+ */
+public class PowerReportTest {
+ private static final double EPSILON = 0.001;
+ private static final double MS_PER_HR = 3600000.0;
+
+ private static final double AVERAGE_MODEM_POWER = ((11+16+19+22+73+132) / 6.0);
+ private static final double GMAIL_MODEM_MAH = ((9925+5577) / (double)(97840+72941))
+ * 5113727 * AVERAGE_MODEM_POWER * (1.0 / 3600 / 1000);
+ private static final double GMAIL_MAH
+ = GMAIL_MODEM_MAH;
+
+ private static final double REMAINDER_MODEM_MAH
+ = (1.0 / 3600 / 1000)
+ * ((3066958 * 16) + (0 * 19) + (34678 * 22) + (1643364 * 73) + (7045084 * 132)
+ + (2443805 * 12)
+ + (4923676 * AVERAGE_MODEM_POWER));
+ private static final double REMAINDER_MAH
+ = REMAINDER_MODEM_MAH;
+
+ private static final double TOTAL_MAH
+ = GMAIL_MAH
+ + REMAINDER_MAH;
+
+ private static InputStream loadPowerProfileStream() {
+ return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+ }
+
+ private static InputStream loadCsvStream() {
+ return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+ }
+
+ private static PowerReport loadPowerReport() throws Exception {
+ final PowerProfile profile = PowerProfile.parse(loadPowerProfileStream());
+ final ActivityReport activity = BatteryStatsReader.parse(loadCsvStream());
+ return PowerReport.createReport(profile, activity);
+ }
+
+ @Test public void testModemApp() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppPower gmail = gmailList.get(0);
+
+ final ModemAppPower modem = (ModemAppPower)gmail.getComponentPower(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(GMAIL_MODEM_MAH, modem.powerMah, EPSILON);
+ }
+
+ @Test public void testModemRemainder() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ final ModemRemainderPower modem
+ = (ModemRemainderPower)remainder.getComponentPower(Component.MODEM);
+ Assert.assertNotNull(modem);
+
+ Assert.assertArrayEquals(new double[] {
+ 3066958 * 16.0 / MS_PER_HR,
+ 0 * 19.0 / MS_PER_HR,
+ 34678 * 22.0 / MS_PER_HR,
+ 1643364 * 73.0 / MS_PER_HR,
+ 7045084 * 132.0 / MS_PER_HR },
+ modem.strengthMah, EPSILON);
+ Assert.assertEquals(2443805 * 12 / MS_PER_HR, modem.scanningMah, EPSILON);
+ Assert.assertEquals(4923676 * AVERAGE_MODEM_POWER / MS_PER_HR, modem.activeMah, EPSILON);
+
+ Assert.assertEquals(REMAINDER_MODEM_MAH, modem.powerMah, EPSILON);
+ }
+
+ @Test public void testAppTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppPower gmail = gmailList.get(0);
+
+ Assert.assertEquals(GMAIL_MAH, gmail.getAppPowerMah(), EPSILON);
+ }
+
+ @Test public void testRemainderTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ Assert.assertEquals(REMAINDER_MAH, remainder.getAppPowerMah(), EPSILON);
+ }
+
+ @Test public void testTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ Assert.assertEquals(TOTAL_MAH, report.getTotalPowerMah(), EPSILON);
+ }
+}
+
diff --git a/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
new file mode 100644
index 0000000..fbcac41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests {@link RawBatteryStats}.
+ */
+public class RawBatteryStatsTest {
+ private static final int BS_VERSION = 32;
+
+ private static InputStream makeCsv(String... lines) {
+ return makeCsv(BS_VERSION, lines);
+ }
+
+ private static InputStream makeCsv(int version, String... lines) {
+ final StringBuilder result = new StringBuilder("9,0,i,vers,");
+ result.append(version);
+ result.append(",177,PPR1.180326.002,PQ1A.181105.015\n");
+ for (String line: lines) {
+ result.append(line);
+ result.append('\n');
+ }
+ return new ByteArrayInputStream(result.toString().getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Test public void testVersion() throws Exception {
+ final InputStream is = makeCsv();
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.Version line = (RawBatteryStats.Version)records.get(0);
+
+ Assert.assertEquals(0, bs.getWarnings().size());
+ Assert.assertEquals(true, line.complete);
+
+ Assert.assertEquals(9, line.lineVersion);
+ Assert.assertEquals(0, line.uid);
+ Assert.assertEquals(RawBatteryStats.Category.INFO, line.category);
+ Assert.assertEquals("vers", line.lineType);
+
+ Assert.assertEquals(BS_VERSION, line.dumpsysVersion);
+ Assert.assertEquals(177, line.parcelVersion);
+ Assert.assertEquals("PPR1.180326.002", line.startPlatformVersion);
+ Assert.assertEquals("PQ1A.181105.015", line.endPlatformVersion);
+ }
+
+ @Test public void testUid() throws Exception {
+ final InputStream is = makeCsv("9,0,i,uid,1000,com.example.app");
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.Uid line = (RawBatteryStats.Uid)records.get(1);
+
+ Assert.assertEquals(1000, line.uidKey);
+ Assert.assertEquals("com.example.app", line.pkg);
+ }
+
+ @Test public void testVarargs() throws Exception {
+ final InputStream is = makeCsv("9,0,i,gmcd,1,2,3,4,5,6,7");
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.GlobalModemController line
+ = (RawBatteryStats.GlobalModemController)records.get(1);
+
+ Assert.assertEquals(1, line.idleMs);
+ Assert.assertEquals(2, line.rxTimeMs);
+ Assert.assertEquals(3, line.powerMaMs);
+ Assert.assertEquals(4, line.txTimeMs.length);
+ Assert.assertEquals(4, line.txTimeMs[0]);
+ Assert.assertEquals(5, line.txTimeMs[1]);
+ Assert.assertEquals(6, line.txTimeMs[2]);
+ Assert.assertEquals(7, line.txTimeMs[3]);
+ }
+}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
index 1d4c435..d368136 100644
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
@@ -28,6 +28,7 @@
import java.io.IOException;
import java.io.PrintStream;
+import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@@ -38,7 +39,9 @@
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
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.element.TypeElement;
/**
@@ -108,10 +111,25 @@
"startline",
"startcol",
"endline",
- "endcol"
+ "endcol",
+ "properties"
);
}
+ private String encodeAnnotationProperties(AnnotationMirror annotation) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
+ : annotation.getElementValues().entrySet()) {
+ if (sb.length() > 0) {
+ sb.append("&");
+ }
+ sb.append(e.getKey().getSimpleName())
+ .append("=")
+ .append(URLEncoder.encode(e.getValue().toString()));
+ }
+ return sb.toString();
+ }
+
/**
* Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
* attached to it. It returns CSV in the format:
@@ -137,7 +155,8 @@
lines.getLineNumber(pair.fst.pos().getStartPosition()),
lines.getColumnNumber(pair.fst.pos().getStartPosition()),
lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
- lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)));
+ lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
+ encodeAnnotationProperties(unsupportedAppUsage));
}
/**
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 8585ae9..88b7e2e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1128,7 +1128,10 @@
hadStringOrChain = true;
fprintf(out, " jbyte* jbyte_array%d;\n", argIndex);
fprintf(out, " const char* str%d;\n", argIndex);
- fprintf(out, " if (arg%d != NULL) {\n", argIndex);
+ fprintf(out,
+ " if (arg%d != NULL && env->GetArrayLength(arg%d) > "
+ "0) {\n",
+ argIndex, argIndex);
fprintf(out,
" jbyte_array%d = "
"env->GetByteArrayElements(arg%d, NULL);\n",
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index 03c0b9a..afe91cd 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -145,6 +145,13 @@
if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
continue
+ misspelled_attributes = [
+ ("translateable", "translatable"),
+ ]
+ for misspelling, expected in misspelled_attributes:
+ if misspelling in child.attrib:
+ error(child, "Misspelled <string> attribute.", misspelling, expected)
+
limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
if limit is None:
info(child, "Missing CHAR LIMIT to aid translation",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3ec8a41..c6acd02 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -58,7 +58,7 @@
*/
oneway void requestActivityInfo(in ResultReceiver result);
- ParceledListSlice getConfiguredNetworks();
+ ParceledListSlice getConfiguredNetworks(String packageName);
ParceledListSlice getPrivilegedConfiguredNetworks();
@@ -90,11 +90,11 @@
List<ScanResult> getScanResults(String callingPackage);
- void disconnect(String packageName);
+ boolean disconnect(String packageName);
- void reconnect(String packageName);
+ boolean reconnect(String packageName);
- void reassociate(String packageName);
+ boolean reassociate(String packageName);
WifiInfo getConnectionInfo(String callingPackage);
@@ -190,8 +190,8 @@
void unregisterNetworkRequestMatchCallback(int callbackIdentifier);
- boolean addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+ int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
- boolean removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+ int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7aff03c..cad6d29 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.net.ConnectivityManager;
@@ -137,6 +138,61 @@
public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3;
/**
+ * Maximum number of active network suggestions allowed per app.
+ * @hide
+ */
+ public static final int NETWORK_SUGGESTIONS_MAX_PER_APP =
+ ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
+
+ /**
+ * Reason code if all of the network suggestions were successfully added or removed.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0;
+
+ /**
+ * Reason code if there was an internal error in the platform while processing the addition or
+ * removal of suggestions.
+ */
+ 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 = 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 = 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 = 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 {}
+
+ /**
* Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
* @hide
*/
@@ -903,8 +959,12 @@
* establish a connection to a remembered access point that is
* within range, and will do periodic scans if there are remembered
* access points but none are in range.
+ *
+ * @deprecated This API is non-functional and will have no impact.
*/
+ @Deprecated
public static final int WIFI_MODE_FULL = 1;
+
/**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
* but the only operation that will be supported is initiation of
@@ -913,28 +973,62 @@
* nor will periodic scans be automatically performed looking for
* remembered access points. Scans must be explicitly requested by
* an application in this mode.
+ *
+ * @deprecated This API is non-functional and will have no impact.
*/
+ @Deprecated
public static final int WIFI_MODE_SCAN_ONLY = 2;
+
/**
- * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode
- * {@link #WIFI_MODE_FULL} but it operates at high performance
- * with minimum packet loss and low packet latency even when
- * the device screen is off. This mode will consume more power
- * and hence should be used only when there is a need for such
- * an active connection.
+ * In this Wi-Fi lock mode, Wi-Fi will not go to power save.
+ * This results in operating with low packet latency.
+ * The lock is active even when the device screen is off or
+ * the acquiring application is running in the background.
+ * This mode will consume more power and hence should be used only
+ * when there is a need for this tradeoff.
* <p>
* An example use case is when a voice connection needs to be
- * kept active even after the device screen goes off. Holding the
- * regular {@link #WIFI_MODE_FULL} lock will keep the wifi
- * connection active, but the connection can be lossy.
+ * kept active even after the device screen goes off.
* Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the
- * duration of the voice call will improve the call quality.
+ * duration of the voice call may improve the call quality.
* <p>
- * When there is no support from the hardware, this lock mode
- * will have the same behavior as {@link #WIFI_MODE_FULL}
+ * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF}
+ * lock will have no impact.
*/
public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
+ /**
+ * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency.
+ * {@link #WIFI_MODE_FULL_LOW_LATENCY} lock has the following limitations:
+ * <ol>
+ * <li>The lock is only active when the screen is on.</li>
+ * <li>The lock is only active when the acquiring app is running in the foreground.</li>
+ * </ol>
+ * Low latency mode optimizes for reduced packet latency,
+ * and as a result other performance measures may suffer when there are trade-offs to make:
+ * <ol>
+ * <li>Battery life may be reduced.</li>
+ * <li>Throughput may be reduced.</li>
+ * <li>Frequency of Wi-Fi scanning may be reduced. This may result in: </li>
+ * <ul>
+ * <li>The device may not roam or switch to the AP with highest signal quality.</li>
+ * <li>Location accuracy may be reduced.</li>
+ * </ul>
+ * </ol>
+ * <p>
+ * Example use cases are real time gaming or virtual reality applications where
+ * low latency is a key factor for user experience.
+ * <p>
+ * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_LOW_LATENCY}
+ * lock will cause the device not to go power save.
+ * <p>
+ * Note: For an app which acquires both {@link #WIFI_MODE_FULL_LOW_LATENCY} and
+ * {@link #WIFI_MODE_FULL_HIGH_PERF} locks, {@link #WIFI_MODE_FULL_LOW_LATENCY}
+ * lock will be effective when app is running in foreground and screen is on,
+ * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise.
+ */
+ public static final int WIFI_MODE_FULL_LOW_LATENCY = 4;
+
/** Anything worse than or equal to this will show 0 bars. */
@UnsupportedAppUsage
private static final int MIN_RSSI = -100;
@@ -1067,7 +1161,7 @@
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
- mService.getConfiguredNetworks();
+ mService.getConfiguredNetworks(mContext.getOpPackageName());
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1126,7 +1220,6 @@
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
try {
@@ -1497,12 +1590,13 @@
* suggestion back using this API.</li>
*
* @param networkSuggestions List of network suggestions provided by the app.
- * @return true on success, false if any of the suggestions match (See
+ * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}.
* {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
* @throws {@link SecurityException} if the caller is missing required permissions.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean addNetworkSuggestions(@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
+ public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
+ @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1510,21 +1604,20 @@
}
}
-
/**
- * Remove a subset of or all of networks from previously provided suggestions by the app to the
- * device.
+ * Remove some or all of the network suggestions that were previously provided by the app.
* See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters.
* See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used.
*
* @param networkSuggestions List of network suggestions to be removed. Pass an empty list
* to remove all the previous suggestions provided by the app.
- * @return true on success, false if any of the suggestions do not match any suggestions
- * previously provided by the app. Any matching suggestions are removed from the device and
- * will not be considered for any further connection attempts.
+ * @return Status code corresponding to the values in
+ * {@link NetworkSuggestionsStatusCode}.
+ * Any matching suggestions are removed from the device and will not be considered for any
+ * further connection attempts.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean removeNetworkSuggestions(
+ public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.removeNetworkSuggestions(
@@ -1535,6 +1628,15 @@
}
/**
+ * Returns the max number of network suggestions that are allowed per app on the device.
+ * @see #addNetworkSuggestions(List)
+ * @see #removeNetworkSuggestions(List)
+ */
+ public int getMaxNumberOfNetworkSuggestionsPerApp() {
+ return NETWORK_SUGGESTIONS_MAX_PER_APP;
+ }
+
+ /**
* Add or update a Passpoint configuration. The configuration provides a credential
* for connecting to Passpoint networks that are operated by the Passpoint
* service provider specified in the configuration.
@@ -1761,8 +1863,7 @@
@Deprecated
public boolean disconnect() {
try {
- mService.disconnect(mContext.getOpPackageName());
- return true;
+ return mService.disconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1786,8 +1887,7 @@
@Deprecated
public boolean reconnect() {
try {
- mService.reconnect(mContext.getOpPackageName());
- return true;
+ return mService.reconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1811,8 +1911,7 @@
@Deprecated
public boolean reassociate() {
try {
- mService.reassociate(mContext.getOpPackageName());
- return true;
+ return mService.reassociate(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1889,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 {
@@ -2132,14 +2233,14 @@
* existing networks. You should assume the network IDs can be different
* after calling this method.
*
- * @return {@code false} Will always return true.
+ * @return {@code false}.
* @deprecated There is no need to call this method -
* {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)}
* and {@link #removeNetwork(int)} already persist the configurations automatically.
*/
@Deprecated
public boolean saveConfiguration() {
- return true;
+ return false;
}
/**
@@ -3406,6 +3507,11 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void connect(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
// Use INVALID_NETWORK_ID for arg1 when passing a config object
@@ -3426,7 +3532,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void connect(int networkId, ActionListener listener) {
if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
@@ -3452,7 +3563,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void save(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
@@ -3471,7 +3587,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void forget(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener));
@@ -3486,7 +3607,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void disable(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener));
@@ -3498,6 +3624,12 @@
* @param SSID, in the format of WifiConfiguration's SSID.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void disableEphemeralNetwork(String SSID) {
if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
try {
@@ -3744,9 +3876,8 @@
/**
* Creates a new WifiLock.
*
- * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
- * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
- * descriptions of the types of Wi-Fi locks.
+ * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL_HIGH_PERF}
+ * and {@link #WIFI_MODE_FULL_LOW_LATENCY} for descriptions of the types of Wi-Fi locks.
* @param tag a tag for the WifiLock to identify it in debugging messages. This string is
* never shown to the user under normal conditions, but should be descriptive
* enough to identify your application and the specific WifiLock within it, if it
@@ -3771,12 +3902,14 @@
* @return a new, unacquired WifiLock with the given tag.
*
* @see WifiLock
+ *
+ * @deprecated This API is non-functional.
*/
+ @Deprecated
public WifiLock createWifiLock(String tag) {
return new WifiLock(WIFI_MODE_FULL, tag);
}
-
/**
* Create a new MulticastLock
*
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index aa8d325..f73b9e5 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -42,8 +42,10 @@
public class WifiNetworkConfigBuilder {
private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
- private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN =
+ private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 =
new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+ private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 =
+ new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS);
private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
@@ -60,14 +62,27 @@
*/
private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
/**
+ * Whether this is an OWE network or not.
+ */
+ private boolean mIsEnhancedOpen;
+ /**
* Pre-shared key for use with WPA-PSK networks.
*/
- private @Nullable String mPskPassphrase;
+ private @Nullable String mWpa2PskPassphrase;
+ /**
+ * Pre-shared key for use with WPA3-SAE networks.
+ */
+ private @Nullable String mWpa3SaePassphrase;
/**
* The enterprise configuration details specifying the EAP method,
- * certificates and other settings associated with the EAP.
+ * certificates and other settings associated with the WPA-EAP networks.
*/
- private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
+ /**
+ * The enterprise configuration details specifying the EAP method,
+ * certificates and other settings associated with the SuiteB networks.
+ */
+ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
/**
* This is a network that does not broadcast its SSID, so an
* SSID-specific probe request must be used for scans.
@@ -94,8 +109,11 @@
public WifiNetworkConfigBuilder() {
mSsidPatternMatcher = null;
mBssidPatternMatcher = null;
- mPskPassphrase = null;
- mEnterpriseConfig = null;
+ mIsEnhancedOpen = false;
+ mWpa2PskPassphrase = null;
+ mWpa3SaePassphrase = null;
+ mWpa2EnterpriseConfig = null;
+ mWpa3EnterpriseConfig = null;
mIsHiddenSSID = false;
mIsAppInteractionRequired = false;
mIsUserInteractionRequired = false;
@@ -173,7 +191,13 @@
* Set the BSSID to use for filtering networks from scan results. Will only match network whose
* BSSID is identical to the specified value.
* <p>
- * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+ * <li>For network requests ({@link NetworkSpecifier}), built using
+ * {@link #buildNetworkSpecifier}, sets the BSSID to use for filtering networks from scan
+ * results. Will only match networks whose BSSID is identical to specified value.</li>
+ * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using
+ * {@link #buildNetworkSuggestion()}, sets a specific BSSID for the network suggestion.
+ * If set, only the specified BSSID with the specified SSID will be considered for connection.
+ * If not set, all BSSIDs with the specified SSID will be considered for connection.</li>
* <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
* {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
*
@@ -188,36 +212,81 @@
}
/**
- * Set the ASCII PSK passphrase for this network. Needed for authenticating to
- * WPA_PSK networks.
+ * Specifies whether this represents an Enhanced Open (OWE) network.
*
- * @param pskPassphrase PSK passphrase of the network.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ */
+ public WifiNetworkConfigBuilder setIsEnhancedOpen() {
+ mIsEnhancedOpen = true;
+ return this;
+ }
+
+ /**
+ * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to
+ * WPA2-PSK networks.
+ *
+ * @param passphrase passphrase of the network.
* @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
* method.
* @throws IllegalArgumentException if the passphrase is not ASCII encodable.
*/
- public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
- checkNotNull(pskPassphrase);
+ public WifiNetworkConfigBuilder setWpa2Passphrase(@NonNull String passphrase) {
+ checkNotNull(passphrase);
final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
- if (!asciiEncoder.canEncode(pskPassphrase)) {
+ if (!asciiEncoder.canEncode(passphrase)) {
throw new IllegalArgumentException("passphrase not ASCII encodable");
}
- mPskPassphrase = pskPassphrase;
+ mWpa2PskPassphrase = passphrase;
+ return this;
+ }
+
+ /**
+ * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to
+ * WPA3-SAE networks.
+ *
+ * @param passphrase passphrase of the network.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+ */
+ public WifiNetworkConfigBuilder setWpa3Passphrase(@NonNull String passphrase) {
+ checkNotNull(passphrase);
+ final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+ if (!asciiEncoder.canEncode(passphrase)) {
+ throw new IllegalArgumentException("passphrase not ASCII encodable");
+ }
+ mWpa3SaePassphrase = passphrase;
return this;
}
/**
* Set the associated enterprise configuration for this network. Needed for authenticating to
- * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+ * WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description.
*
* @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
* @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
* method.
*/
- public WifiNetworkConfigBuilder setEnterpriseConfig(
+ public WifiNetworkConfigBuilder setWpa2EnterpriseConfig(
@NonNull WifiEnterpriseConfig enterpriseConfig) {
checkNotNull(enterpriseConfig);
- mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ return this;
+ }
+
+ /**
+ * Set the associated enterprise configuration for this network. Needed for authenticating to
+ * WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description.
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ */
+ public WifiNetworkConfigBuilder setWpa3EnterpriseConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
return this;
}
@@ -324,16 +393,38 @@
configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
}
- private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
- if (!TextUtils.isEmpty(mPskPassphrase)) {
- // WPA_PSK network.
+ private void setSecurityParamsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+ if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
- } else if (mEnterpriseConfig != null) {
- // WPA_EAP network
+ // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+ configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
+ } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+ // PMF mandatory for SAE.
+ configuration.requirePMF = true;
+ // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+ configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
+ } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
- } else {
- // Open network
+ configuration.enterpriseConfig = mWpa2EnterpriseConfig;
+ } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+ // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
+ configuration.allowedGroupMgmtCiphers.set(
+ WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
+ configuration.allowedSuiteBCiphers.set(
+ WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+ configuration.allowedSuiteBCiphers.set(
+ WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+ configuration.requirePMF = true;
+ configuration.enterpriseConfig = mWpa3EnterpriseConfig;
+ } else if (mIsEnhancedOpen) { // OWE network
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+ // PMF mandatory.
+ configuration.requirePMF = true;
+ } else { // Open network
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
}
}
@@ -349,12 +440,10 @@
if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
}
- setKeyMgmtInWifiConfiguration(wifiConfiguration);
- // WifiConfiguration.preSharedKey needs quotes around ASCII password.
- if (mPskPassphrase != null) {
- wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
+ if (mBssidPatternMatcher.second == MATCH_EXACT_BSSID_PATTERN_MASK) {
+ wifiConfiguration.BSSID = mBssidPatternMatcher.first.toString();
}
- wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+ setSecurityParamsInWifiConfiguration(wifiConfiguration);
wifiConfiguration.hiddenSSID = mIsHiddenSSID;
wifiConfiguration.priority = mPriority;
wifiConfiguration.meteredOverride =
@@ -382,7 +471,10 @@
&& mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
return true;
}
- if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) {
+ if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN1)) {
+ return true;
+ }
+ if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN2)) {
return true;
}
return false;
@@ -396,6 +488,30 @@
return false;
}
+ private boolean hasSetMatchExactPattern() {
+ // exact ssid match with either match-all bssid or match-exact bssid.
+ if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL
+ && (mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)
+ || mBssidPatternMatcher.second.equals(MATCH_EXACT_BSSID_PATTERN_MASK))) {
+ return true;
+ }
+ return false;
+ }
+
+ private void validateSecurityParams() {
+ int numSecurityTypes = 0;
+ numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
+ numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0;
+ numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
+ numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
+ numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
+ if (numSecurityTypes > 1) {
+ throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
+ + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig"
+ + " can be invoked for network specifier");
+ }
+ }
+
/**
* Create a specifier object used to request a Wi-Fi network. The generated
* {@link NetworkSpecifier} should be used in
@@ -464,10 +580,7 @@
+ "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
+ "specifier");
}
- if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
- throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
- + " be invoked for network specifier");
- }
+ validateSecurityParams();
return new WifiNetworkSpecifier(
mSsidPatternMatcher,
@@ -477,9 +590,42 @@
}
/**
- * Create a network suggestion object use in
- * {@link WifiManager#addNetworkSuggestions(List)}.
+ * Create a network suggestion object use in {@link WifiManager#addNetworkSuggestions(List)}.
* See {@link WifiNetworkSuggestion}.
+ *<p>
+ * Note: Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
+ * using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to the
+ * platform.
+ * </p>
+ *
+ * For example:
+ * To provide credentials for one open, one WPA2 and one WPA3 network with their
+ * corresponding SSID's:
+ * {@code
+ * final WifiNetworkSuggestion suggestion1 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test111111")
+ * .buildNetworkSuggestion()
+ * final WifiNetworkSuggestion suggestion2 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test222222")
+ * .setWpa2Passphrase("test123456")
+ * .buildNetworkSuggestion()
+ * final WifiNetworkSuggestion suggestion3 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test333333")
+ * .setWpa3Passphrase("test6789")
+ * .buildNetworkSuggestion()
+ * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion> {{
+ * add(suggestion1);
+ * add(suggestion2);
+ * add(suggestion3);
+ * }};
+ * final WifiManager wifiManager =
+ * context.getSystemService(Context.WIFI_SERVICE);
+ * wifiManager.addNetworkSuggestions(suggestionsList);
+ * ...
+ * }
*
* @return Instance of {@link WifiNetworkSuggestion}.
* @throws IllegalStateException on invalid params set.
@@ -488,15 +634,15 @@
if (mSsidPatternMatcher == null) {
throw new IllegalStateException("setSsid should be invoked for suggestion");
}
- if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL
- || mBssidPatternMatcher != null) {
- throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+ setMatchAnyPatternIfUnset();
+ if (!hasSetMatchExactPattern()) {
+ throw new IllegalStateException("none of setSsidPattern/setBssidPattern are"
+ " allowed for suggestion");
}
- if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
- throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
- + "be invoked for suggestion");
+ if (hasSetMatchNonePattern()) {
+ throw new IllegalStateException("cannot set match-none for suggestion");
}
+ validateSecurityParams();
return new WifiNetworkSuggestion(
buildWifiConfiguration(),
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 25121e2..760f1e6 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -104,8 +104,8 @@
@Override
public int hashCode() {
- return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement,
- suggestorUid);
+ return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
+ wifiConfiguration.allowedKeyManagement, suggestorUid);
}
/**
@@ -121,6 +121,7 @@
}
WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+ && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
&& Objects.equals(this.wifiConfiguration.allowedKeyManagement,
lhs.wifiConfiguration.allowedKeyManagement)
&& suggestorUid == lhs.suggestorUid;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 529548f..acc0518 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -184,6 +184,9 @@
public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
/** {@hide} */
public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource";
+ /** {@hide} */
+ public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName";
+
/**
* scan configuration parameters to be sent to {@link #startBackgroundScan}
*/
@@ -250,6 +253,14 @@
*/
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public int type = TYPE_LOW_LATENCY;
+ /**
+ * This scan request may ignore location settings while receiving scans. This should only
+ * be used in emergency situations.
+ * {@hide}
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ public boolean ignoreLocationSettings;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -267,6 +278,7 @@
dest.writeInt(stepCount);
dest.writeInt(isPnoScan ? 1 : 0);
dest.writeInt(type);
+ dest.writeInt(ignoreLocationSettings ? 1 : 0);
if (channels != null) {
dest.writeInt(channels.length);
for (int i = 0; i < channels.length; i++) {
@@ -301,6 +313,7 @@
settings.stepCount = in.readInt();
settings.isPnoScan = in.readInt() == 1;
settings.type = in.readInt();
+ settings.ignoreLocationSettings = in.readInt() == 1;
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {
@@ -344,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[];
@@ -361,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;
}
@@ -374,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];
@@ -397,8 +411,8 @@
}
/** {@hide} */
- public boolean isAllChannelsScanned() {
- return mAllChannelsScanned;
+ public int getBandScanned() {
+ return mBandScanned;
}
public ScanResult[] getResults() {
@@ -416,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];
@@ -434,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) {
@@ -746,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);
@@ -798,6 +813,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
}
@@ -812,8 +828,11 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams);
}
+
/**
* reports currently available scan results on appropriate listeners
* @return true if all scan results were reported correctly
@@ -821,7 +840,10 @@
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public boolean getScanResults() {
validateChannel();
- Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ Message reply =
+ mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams);
return reply.what == CMD_OP_SUCCEEDED;
}
@@ -856,6 +878,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
@@ -870,7 +893,9 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams);
}
/**
@@ -879,7 +904,10 @@
*/
public List<ScanResult> getSingleScanResults() {
validateChannel();
- Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0,
+ scanParams);
if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
return Arrays.asList(((ParcelableScanResults) reply.obj).getResults());
}
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 25dcdd8..893b19c 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -49,7 +49,7 @@
/**
* SSID of the network to connect for service sign-up.
*/
- private final WifiSsid mOsuSsid;
+ private WifiSsid mOsuSsid;
/**
* Friendly name of the OSU provider.
@@ -130,6 +130,10 @@
return mOsuSsid;
}
+ public void setOsuSsid(WifiSsid osuSsid) {
+ mOsuSsid = osuSsid;
+ }
+
public String getFriendlyName() {
return mFriendlyName;
}
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 5c9db53..a62d63c 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -150,6 +150,12 @@
public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22;
/**
+ * The reason code for provisioning failure when an {@link OsuProvider} is not found for
+ * provisioning.
+ */
+ public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23;
+
+ /**
* The status code for provisioning flow to indicate connecting to OSU AP
*/
public static final int OSU_STATUS_AP_CONNECTING = 1;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6772096..6631fa8 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -16,10 +16,16 @@
package android.net.wifi.p2p;
+import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
+import android.net.MacAddress;
import android.net.wifi.WpsInfo;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* A class representing a Wi-Fi P2p configuration for setting up a connection
@@ -38,12 +44,46 @@
*/
public WpsInfo wps;
+ /**
+ * The network name of a group, should be configured by helper method
+ */
+ /** @hide */
+ public String networkName = "";
+
+ /**
+ * The passphrase of a group, should be configured by helper method
+ */
+ /** @hide */
+ public String passphrase = "";
+
+ /**
+ * The required band for Group Owner
+ */
+ /** @hide */
+ public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
+
/** @hide */
public static final int MAX_GROUP_OWNER_INTENT = 15;
/** @hide */
@UnsupportedAppUsage
public static final int MIN_GROUP_OWNER_INTENT = 0;
+ /** @hide */
+ @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
+ GROUP_OWNER_BAND_AUTO,
+ GROUP_OWNER_BAND_2GHZ,
+ GROUP_OWNER_BAND_5GHZ
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GroupOwnerBandType {}
+
+ /**
+ * Recognized Group Owner required band.
+ */
+ public static final int GROUP_OWNER_BAND_AUTO = 0;
+ public static final int GROUP_OWNER_BAND_2GHZ = 1;
+ public static final int GROUP_OWNER_BAND_5GHZ = 2;
+
/**
* This is an integer value between 0 and 15 where 0 indicates the least
* inclination to be a group owner and 15 indicates the highest inclination
@@ -115,6 +155,10 @@
sbuf.append("\n wps: ").append(wps);
sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
sbuf.append("\n persist: ").append(netId);
+ sbuf.append("\n networkName: ").append(networkName);
+ sbuf.append("\n passphrase: ").append(
+ TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
+ sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
return sbuf.toString();
}
@@ -130,6 +174,9 @@
wps = new WpsInfo(source.wps);
groupOwnerIntent = source.groupOwnerIntent;
netId = source.netId;
+ networkName = source.networkName;
+ passphrase = source.passphrase;
+ groupOwnerBand = source.groupOwnerBand;
}
}
@@ -139,6 +186,9 @@
dest.writeParcelable(wps, flags);
dest.writeInt(groupOwnerIntent);
dest.writeInt(netId);
+ dest.writeString(networkName);
+ dest.writeString(passphrase);
+ dest.writeInt(groupOwnerBand);
}
/** Implement the Parcelable interface */
@@ -150,6 +200,9 @@
config.wps = (WpsInfo) in.readParcelable(null);
config.groupOwnerIntent = in.readInt();
config.netId = in.readInt();
+ config.networkName = in.readString();
+ config.passphrase = in.readString();
+ config.groupOwnerBand = in.readInt();
return config;
}
@@ -157,4 +210,140 @@
return new WifiP2pConfig[size];
}
};
+
+ /**
+ * Builder used to build {@link WifiP2pConfig} objects for
+ * creating or joining a group.
+ */
+ public static final class Builder {
+
+ private static final MacAddress MAC_ANY_ADDRESS =
+ MacAddress.fromString("00:00:00:00:00:00");
+
+ private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
+ private String mNetworkName = "";
+ private String mPassphrase = "";
+ private int mGroupOwnerBand = GROUP_OWNER_BAND_AUTO;
+ private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+
+ /**
+ * Specify the peer's MAC address. If not set, the device will
+ * try to find a peer whose SSID matches the network name as
+ * specified by {@link #setNetworkName(String)}. Specifying null will
+ * reset the peer's MAC address to "00:00:00:00:00:00".
+ * <p>
+ * Optional. "00:00:00:00:00:00" by default.
+ *
+ * @param deviceAddress the peer's MAC address.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setDeviceAddress(MacAddress deviceAddress) {
+ if (deviceAddress == null) {
+ mDeviceAddress = MAC_ANY_ADDRESS;
+ } else {
+ mDeviceAddress = deviceAddress;
+ }
+ return this;
+ }
+
+ /**
+ * Specify the network name, a.k.a. group name,
+ * for creating or joining a group.
+ * <p>
+ * Must be called - an empty network name is not valid.
+ *
+ * @param networkName network name of a group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setNetworkName(String networkName) {
+ if (TextUtils.isEmpty(networkName)) {
+ throw new IllegalArgumentException(
+ "network name must be non-empty.");
+ }
+ mNetworkName = networkName;
+ return this;
+ }
+
+ /**
+ * Specify the passphrase for creating or joining a group.
+ * <p>
+ * Must be called - an empty passphrase is not valid.
+ *
+ * @param passphrase the passphrase of a group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setPassphrase(String passphrase) {
+ if (TextUtils.isEmpty(passphrase)) {
+ throw new IllegalArgumentException(
+ "passphrase must be non-empty.");
+ }
+ mPassphrase = passphrase;
+ return this;
+ }
+
+ /**
+ * Specify the band to use for creating the group. This method only applies when
+ * creating a group as Group Owner using {@link WifiP2pManager#createGroup}.
+ * The band should be {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ},
+ * or allow the system to pick the band by specifying {@link #GROUP_OWNER_BAND_AUTO}.
+ * If the Group Owner cannot create a group in the specified band, the operation will fail.
+ * <p>
+ * Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
+ *
+ * @param band the required band of group owner.
+ * This should be one of {@link #GROUP_OWNER_BAND_AUTO},
+ * {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setGroupOwnerBand(int band) {
+ mGroupOwnerBand = band;
+ return this;
+ }
+
+ /**
+ * Specify that the group configuration be persisted (i.e. saved).
+ * By default the group configuration will not be saved.
+ * <p>
+ * Optional. false by default.
+ *
+ * @param persistent is this group persistent group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder enablePersistentMode(boolean persistent) {
+ if (persistent) {
+ mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
+ } else {
+ mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+ }
+ return this;
+ }
+
+ /**
+ * Build {@link WifiP2pConfig} given the current requests made on the builder.
+ * @return {@link WifiP2pConfig} constructed based on builder method calls.
+ */
+ public WifiP2pConfig build() {
+ if (TextUtils.isEmpty(mNetworkName)) {
+ throw new IllegalStateException(
+ "network name must be non-empty.");
+ }
+ if (TextUtils.isEmpty(mPassphrase)) {
+ throw new IllegalStateException(
+ "passphrase must be non-empty.");
+ }
+
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = mDeviceAddress.toString();
+ config.networkName = mNetworkName;
+ config.passphrase = mPassphrase;
+ config.groupOwnerBand = mGroupOwnerBand;
+ config.netId = mNetId;
+ return config;
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index f58a006..e0442f2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -16,15 +16,16 @@
package android.net.wifi.p2p;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
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;
+import android.net.NetworkInfo;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse;
@@ -49,6 +50,8 @@
import dalvik.system.CloseGuard;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -158,6 +161,14 @@
*/
public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
+ /** @hide */
+ @IntDef({
+ WIFI_P2P_STATE_DISABLED,
+ WIFI_P2P_STATE_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WifiP2pState {
+ }
+
/**
* Wi-Fi p2p is disabled.
*
@@ -250,6 +261,14 @@
*/
public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
+ /** @hide */
+ @IntDef({
+ WIFI_P2P_DISCOVERY_STOPPED,
+ WIFI_P2P_DISCOVERY_STARTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WifiP2pDiscoveryState {
+ }
+
/**
* p2p discovery has stopped
*
@@ -502,6 +521,21 @@
/** @hide */
public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED = BASE + 89;
+ /** @hide */
+ public static final int REQUEST_P2P_STATE = BASE + 90;
+ /** @hide */
+ public static final int RESPONSE_P2P_STATE = BASE + 91;
+
+ /** @hide */
+ public static final int REQUEST_DISCOVERY_STATE = BASE + 92;
+ /** @hide */
+ public static final int RESPONSE_DISCOVERY_STATE = BASE + 93;
+
+ /** @hide */
+ public static final int REQUEST_NETWORK_INFO = BASE + 94;
+ /** @hide */
+ public static final int RESPONSE_NETWORK_INFO = BASE + 95;
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -690,6 +724,43 @@
public void onHandoverMessageAvailable(String handoverMessage);
}
+ /** Interface for callback invocation when p2p state is available
+ * in response to {@link #requestP2pState}.
+ */
+ public interface P2pStateListener {
+ /**
+ * The requested p2p state is available.
+ * @param state Wi-Fi p2p state
+ * @see #WIFI_P2P_STATE_DISABLED
+ * @see #WIFI_P2P_STATE_ENABLED
+ */
+ void onP2pStateAvailable(@WifiP2pState int state);
+ }
+
+ /** Interface for callback invocation when p2p state is available
+ * in response to {@link #requestDiscoveryState}.
+ */
+ public interface DiscoveryStateListener {
+ /**
+ * The requested p2p discovery state is available.
+ * @param state Wi-Fi p2p discovery state
+ * @see #WIFI_P2P_DISCOVERY_STARTED
+ * @see #WIFI_P2P_DISCOVERY_STOPPED
+ */
+ void onDiscoveryStateAvailable(@WifiP2pDiscoveryState int state);
+ }
+
+ /** Interface for callback invocation when {@link android.net.NetworkInfo} is available
+ * in response to {@link #requestNetworkInfo}.
+ */
+ public interface NetworkInfoListener {
+ /**
+ * The requested {@link android.net.NetworkInfo} is available
+ * @param networkInfo Wi-Fi p2p {@link android.net.NetworkInfo}
+ */
+ void onNetworkInfoAvailable(NetworkInfo networkInfo);
+ }
+
/**
* Interface for callback invocation when ongoing peer info is available
* @hide
@@ -889,6 +960,24 @@
.onOngoingPeerAvailable(peerConfig);
}
break;
+ case RESPONSE_P2P_STATE:
+ if (listener != null) {
+ ((P2pStateListener) listener)
+ .onP2pStateAvailable(message.arg1);
+ }
+ break;
+ case RESPONSE_DISCOVERY_STATE:
+ if (listener != null) {
+ ((DiscoveryStateListener) listener)
+ .onDiscoveryStateAvailable(message.arg1);
+ }
+ break;
+ case RESPONSE_NETWORK_INFO:
+ if (listener != null) {
+ ((NetworkInfoListener) listener)
+ .onNetworkInfoAvailable((NetworkInfo) message.obj);
+ }
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -1126,6 +1215,38 @@
}
/**
+ * Create a p2p group with the current device as the group owner. This essentially creates
+ * an access point that can accept connections from legacy clients as well as other p2p
+ * devices.
+ *
+ * <p> An app should use {@link WifiP2pConfig.Builder} to build the configuration
+ * for a group.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * This function would normally not be used unless the current device needs
+ * to form a p2p group as a Group Owner and allow peers to join it as either
+ * Group Clients or legacy Wi-Fi STAs.
+ *
+ * <p> The function call immediately returns after sending a group creation request
+ * to the framework. The application is notified of a success or failure to initiate
+ * group creation through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p> Application can request for the group details with {@link #requestGroupInfo}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param config the configuration of a p2p group.
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void createGroup(@NonNull Channel c,
+ @Nullable WifiP2pConfig config,
+ @Nullable ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(CREATE_GROUP, 0,
+ c.putListener(listener), config);
+ }
+
+ /**
* Remove the current p2p group.
*
* <p> The function call immediately returns after sending a group removal request
@@ -1573,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);
@@ -1616,4 +1736,68 @@
c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0,
c.putListener(listener), config);
}
+
+ /**
+ * Request p2p enabled state.
+ *
+ * <p> This state indicates whether Wi-Fi p2p is enabled or disabled.
+ * The valid value is one of {@link #WIFI_P2P_STATE_DISABLED} or
+ * {@link #WIFI_P2P_STATE_ENABLED}. The state is returned using the
+ * {@link P2pStateListener} listener.
+ *
+ * <p> This state is also included in the {@link #WIFI_P2P_STATE_CHANGED_ACTION}
+ * broadcast event with extra {@link #EXTRA_WIFI_STATE}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param listener for callback when p2p state is available..
+ */
+ public void requestP2pState(@NonNull Channel c,
+ @NonNull P2pStateListener listener) {
+ checkChannel(c);
+ if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+ c.mAsyncChannel.sendMessage(REQUEST_P2P_STATE, 0, c.putListener(listener));
+ }
+
+ /**
+ * Request p2p discovery state.
+ *
+ * <p> This state indicates whether p2p discovery has started or stopped.
+ * The valid value is one of {@link #WIFI_P2P_DISCOVERY_STARTED} or
+ * {@link #WIFI_P2P_DISCOVERY_STOPPED}. The state is returned using the
+ * {@link DiscoveryStateListener} listener.
+ *
+ * <p> This state is also included in the {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION}
+ * broadcast event with extra {@link #EXTRA_DISCOVERY_STATE}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param listener for callback when discovery state is available..
+ */
+ public void requestDiscoveryState(@NonNull Channel c,
+ @NonNull DiscoveryStateListener listener) {
+ checkChannel(c);
+ if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+ c.mAsyncChannel.sendMessage(REQUEST_DISCOVERY_STATE, 0, c.putListener(listener));
+ }
+
+ /**
+ * Request network info.
+ *
+ * <p> This method provides the network info in the form of a {@link android.net.NetworkInfo}.
+ * {@link android.net.NetworkInfo#isAvailable()} indicates the p2p availability and
+ * {@link android.net.NetworkInfo#getDetailedState()} reports the current fine-grained state
+ * of the network. This {@link android.net.NetworkInfo} is returned using the
+ * {@link NetworkInfoListener} listener.
+ *
+ * <p> This information is also included in the {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION}
+ * broadcast event with extra {@link #EXTRA_NETWORK_INFO}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param listener for callback when network info is available..
+ */
+ public void requestNetworkInfo(@NonNull Channel c,
+ @NonNull NetworkInfoListener listener) {
+ checkChannel(c);
+ if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+ c.mAsyncChannel.sendMessage(REQUEST_NETWORK_INFO, 0, c.putListener(listener));
+ }
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 04bc557..0f4e3a8 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -73,7 +73,7 @@
}
@Override
- public ParceledListSlice getConfiguredNetworks() {
+ public ParceledListSlice getConfiguredNetworks(String packageName) {
throw new UnsupportedOperationException();
}
@@ -188,17 +188,17 @@
}
@Override
- public void disconnect(String packageName) {
+ public boolean disconnect(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void reconnect(String packageName) {
+ public boolean reconnect(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void reassociate(String packageName) {
+ public boolean reassociate(String packageName) {
throw new UnsupportedOperationException();
}
@@ -442,13 +442,13 @@
}
@Override
- public boolean addNetworkSuggestions(
+ public int addNetworkSuggestions(
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
@Override
- public boolean removeNetworkSuggestions(
+ public int removeNetworkSuggestions(
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8fe5af9..13c8c9e 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1298,13 +1298,26 @@
*/
@Test
public void addRemoveNetworkSuggestions() throws Exception {
- when(mWifiService.addNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
- when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
+ when(mWifiService.addNetworkSuggestions(any(List.class), anyString()))
+ .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+ when(mWifiService.removeNetworkSuggestions(any(List.class), anyString()))
+ .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
- assertTrue(mWifiManager.addNetworkSuggestions(new ArrayList<>()));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.addNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
- assertTrue(mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
}
+
+ /**
+ * Verify call to {@link WifiManager#getMaxNumberOfNetworkSuggestionsPerApp()}.
+ */
+ @Test
+ public void getMaxNumberOfNetworkSuggestionsPerApp() {
+ assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
+ mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 8980ddb..2505499 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
@@ -81,11 +82,11 @@
* pattern.
*/
@Test
- public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+ public void testWifiNetworkSpecifierBuilderForWpa2PskNetworkWithBssidPattern() {
NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
.setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK))
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.buildNetworkSpecifier();
assertTrue(specifier instanceof WifiNetworkSpecifier);
@@ -119,7 +120,7 @@
* SSID and BSSID pattern.
*/
@Test
- public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+ public void testWifiNetworkSpecifierBuilderForWpa2EapHiddenNetworkWithSsidAndBssid() {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
@@ -127,7 +128,7 @@
NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
.setBssid(MacAddress.fromString(TEST_BSSID))
- .setEnterpriseConfig(enterpriseConfig)
+ .setWpa2EnterpriseConfig(enterpriseConfig)
.setIsHiddenSsid()
.buildNetworkSpecifier();
@@ -174,14 +175,14 @@
}
/**
- * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+ * Ensure {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} throws an exception
* when the string is not ASCII encodable.
*/
@Test(expected = IllegalArgumentException.class)
- public void testSetPskPassphraseWithNonAsciiString() {
+ public void testSetWpa2PasphraseWithNonAsciiString() {
new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase("salvē")
+ .setWpa2Passphrase("salvē")
.buildNetworkSpecifier();
}
@@ -243,7 +244,7 @@
* when match-none SSID pattern is set.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() {
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern1() {
new WifiNetworkConfigBuilder()
.setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL))
.buildNetworkSpecifier();
@@ -251,10 +252,21 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none SSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern2() {
+ new WifiNetworkConfigBuilder()
+ .setSsid("")
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
* when match-none BSSID pattern is set.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() {
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern1() {
new WifiNetworkConfigBuilder()
.setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS)
.buildNetworkSpecifier();
@@ -262,6 +274,28 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none BSSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern2() {
+ new WifiNetworkConfigBuilder()
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none BSSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() {
+ new WifiNetworkConfigBuilder()
+ .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
* when SSID pattern is set for hidden network.
*/
@Test(expected = IllegalStateException.class)
@@ -275,15 +309,15 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
- * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
- * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+ * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+ public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndEnterpriseConfig() {
new WifiNetworkConfigBuilder()
.setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
- .setPskPassphrase(TEST_PRESHARED_KEY)
- .setEnterpriseConfig(new WifiEnterpriseConfig())
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setWpa2EnterpriseConfig(new WifiEnterpriseConfig())
.buildNetworkSpecifier();
}
@@ -375,10 +409,11 @@
* app interaction and has a priority of zero set.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+ public void
+ testWifiNetworkSuggestionBuilderForWpa2EapNetworkWithPriorityAndReqAppInteraction() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsAppInteractionRequired()
.setPriority(0)
.buildNetworkSuggestion();
@@ -401,10 +436,11 @@
* user interaction and is metered.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+ public void
+ testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithMeteredAndReqUserInteraction() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsUserInteractionRequired()
.setIsMetered()
.buildNetworkSuggestion();
@@ -422,6 +458,76 @@
}
/**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetworkWithBssid() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setBssid(MacAddress.fromString(TEST_BSSID))
+ .setIsEnhancedOpen()
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertEquals(TEST_BSSID, suggestion.wifiConfiguration.BSSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.OWE));
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SAE network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3PskNetwork() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SAE));
+ assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+ suggestion.wifiConfiguration.preSharedKey);
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ }
+
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseConfig(enterpriseConfig)
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+ .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
+ assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+ .get(WifiConfiguration.SuiteBCipher.ECDHE_RSA));
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ }
+
+ /**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
* when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
*/
@@ -434,7 +540,7 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set.
+ * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
*/
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSuggestionBuilderWithBssidPattern() {
@@ -447,18 +553,6 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
- */
- @Test(expected = IllegalStateException.class)
- public void testWifiNetworkSuggestionBuilderWithBssid() {
- new WifiNetworkConfigBuilder()
- .setSsid(TEST_SSID)
- .setBssid(MacAddress.fromString(TEST_BSSID))
- .buildNetworkSuggestion();
- }
-
- /**
- * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
* when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
*/
@Test(expected = IllegalStateException.class)
@@ -468,6 +562,41 @@
}
/**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setSsid(String)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidSsid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid("")
+ .buildNetworkSuggestion();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidBroadcastBssid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
+ .buildNetworkSuggestion();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .buildNetworkSuggestion();
+ }
+
+ /**
* Ensure {@link WifiNetworkConfigBuilder#setPriority(int)} throws an exception
* when the value is negative.
*/
@@ -478,4 +607,46 @@
.setPriority(-1)
.buildNetworkSuggestion();
}
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndWpa3Passphrase() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setIsEnhancedOpen(} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnhancedOpen() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setIsEnhancedOpen()
+ .buildNetworkSpecifier();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 6bab60d..5cc8217 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -29,6 +29,7 @@
@SmallTest
public class WifiNetworkSuggestionTest {
private static final String TEST_SSID = "\"Test123\"";
+ private static final String TEST_BSSID = "12:12:12:12:12:12";
private static final String TEST_SSID_1 = "\"Test1234\"";
/**
@@ -38,6 +39,7 @@
public void testWifiNetworkSuggestionParcel() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion(configuration, false, true, 0);
@@ -65,18 +67,20 @@
/**
* Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same
- * SSID, key mgmt and UID.
+ * SSID, BSSID, key mgmt and UID.
*/
@Test
public void testWifiNetworkSuggestionEqualsSame() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion(configuration, true, false, 0);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
+ configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
new WifiNetworkSuggestion(configuration1, false, true, 0);
@@ -86,7 +90,7 @@
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * key mgmt and UID, but different SSID.
+ * BSSID, key mgmt and UID, but different SSID.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() {
@@ -107,7 +111,29 @@
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * SSID and UID, but different key mgmt.
+ * SSID, key mgmt and UID, but different BSSID.
+ */
+ @Test
+ public void testWifiNetworkSuggestionEqualsFailsWhenBssidIsDifferent() {
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion =
+ new WifiNetworkSuggestion(configuration, false, false, 0);
+
+ WifiConfiguration configuration1 = new WifiConfiguration();
+ configuration1.SSID = TEST_SSID;
+ configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion1 =
+ new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+ assertNotEquals(suggestion, suggestion1);
+ }
+
+ /**
+ * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+ * SSID, BSSID and UID, but different key mgmt.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() {
@@ -128,7 +154,7 @@
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * SSID and key mgmt, but different UID.
+ * SSID, BSSID and key mgmt, but different UID.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() {
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);
+ }
}