Merge "Use empty data if file in zip has length 0"
diff --git a/Android.bp b/Android.bp
index 1c59e2b..faad6f3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -77,6 +77,7 @@
"core/java/android/app/ISearchManager.aidl",
"core/java/android/app/ISearchManagerCallback.aidl",
"core/java/android/app/IServiceConnection.aidl",
+ "core/java/android/app/ISmsAppService.aidl",
"core/java/android/app/IStopUserCallback.aidl",
"core/java/android/app/job/IJobCallback.aidl",
"core/java/android/app/job/IJobScheduler.aidl",
@@ -151,8 +152,8 @@
":libcamera_client_framework_aidl",
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptService.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl",
+ "core/java/android/hardware/biometrics/IBiometricService.aidl",
+ "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
@@ -368,6 +369,7 @@
"core/java/com/android/internal/app/IAppOpsService.aidl",
"core/java/com/android/internal/app/IBatteryStats.aidl",
"core/java/com/android/internal/app/ISoundTriggerService.aidl",
+ "core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl",
"core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
"core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
"core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
@@ -514,11 +516,14 @@
"telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
+ "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
"telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
"telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
+ "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+ "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
"telephony/java/android/telephony/INetworkService.aidl",
"telephony/java/android/telephony/INetworkServiceCallback.aidl",
"telephony/java/com/android/ims/internal/IImsCallSession.aidl",
@@ -702,7 +707,6 @@
// Loaded with System.loadLibrary by android.view.textclassifier
required: [
- "libtextclassifier",
"libmedia2_jni",
],
@@ -806,12 +810,16 @@
java_library {
name: "ext",
installable: true,
- no_framework_libs: true,
+ sdk_version: "core_current",
static_libs: [
"libphonenumber-platform",
"nist-sip",
"tagsoup",
"rappor",
+ "libtextclassifier-java",
+ ],
+ required: [
+ "libtextclassifier",
],
dxflags: ["--core-library"],
}
@@ -1281,6 +1289,9 @@
}
droiddoc {
+ // Please sync with android-api-council@ before making any changes for the name property below.
+ // Since there's cron jobs that fetch offline-sdk-referenceonly-docs-docs.zip periodically.
+ // See b/116221385 for reference.
name: "offline-sdk-referenceonly-docs",
defaults: ["framework-docs-default"],
srcs: [
@@ -1297,6 +1308,9 @@
}
droiddoc {
+ // Please sync with android-api-council@ before making any changes for the name property below.
+ // Since there's cron jobs that fetch offline-system-sdk-referenceonly-docs-docs.zip periodically.
+ // See b/116221385 for reference.
name: "offline-system-sdk-referenceonly-docs",
defaults: ["framework-docs-default"],
srcs: [
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 2247e43..6deda0c 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -247,6 +247,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediadrm.signer.jar)
$(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)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index de83f3e..ff40f75 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,11 @@
services/print/
services/usb/
telephony/
+ tests/ActivityViewTest/
+ tests/LotsOfApps/
+ tests/NativeProcessesMemoryTest/
+ tests/OdmApps/
+ tests/SystemMemoryTest/
wifi/
api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
diff --git a/api/current.txt b/api/current.txt
index 90ceee6..e6a31af 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -7,6 +7,7 @@
public static final class Manifest.permission {
ctor public Manifest.permission();
field public static final java.lang.String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
+ field public static final java.lang.String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -37,6 +38,7 @@
field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
+ field public static final java.lang.String BIND_SMS_APP_SERVICE = "android.permission.BIND_SMS_APP_SERVICE";
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
@@ -772,9 +774,11 @@
field public static final int isFeatureSplit = 16844123; // 0x101055b
field public static final int isGame = 16843764; // 0x10103f4
field public static final int isIndicator = 16843079; // 0x1010147
+ field public static final int isLightTheme = 16844175; // 0x101058f
field public static final int isModifier = 16843334; // 0x1010246
field public static final int isRepeatable = 16843336; // 0x1010248
field public static final int isScrollContainer = 16843342; // 0x101024e
+ field public static final int isSplitRequired = 16844176; // 0x1010590
field public static final int isStatic = 16844122; // 0x101055a
field public static final int isSticky = 16843335; // 0x1010247
field public static final int isolatedProcess = 16843689; // 0x10103a9
@@ -4296,6 +4300,20 @@
method public abstract void onActivityCreated(android.app.Activity, android.os.Bundle);
method public abstract void onActivityDestroyed(android.app.Activity);
method public abstract void onActivityPaused(android.app.Activity);
+ method public default void onActivityPostCreated(android.app.Activity, android.os.Bundle);
+ method public default void onActivityPostDestroyed(android.app.Activity);
+ method public default void onActivityPostPaused(android.app.Activity);
+ method public default void onActivityPostResumed(android.app.Activity);
+ method public default void onActivityPostSaveInstanceState(android.app.Activity, android.os.Bundle);
+ method public default void onActivityPostStarted(android.app.Activity);
+ method public default void onActivityPostStopped(android.app.Activity);
+ method public default void onActivityPreCreated(android.app.Activity, android.os.Bundle);
+ method public default void onActivityPreDestroyed(android.app.Activity);
+ method public default void onActivityPrePaused(android.app.Activity);
+ method public default void onActivityPreResumed(android.app.Activity);
+ method public default void onActivityPreSaveInstanceState(android.app.Activity, android.os.Bundle);
+ method public default void onActivityPreStarted(android.app.Activity);
+ method public default void onActivityPreStopped(android.app.Activity);
method public abstract void onActivityResumed(android.app.Activity);
method public abstract void onActivitySaveInstanceState(android.app.Activity, android.os.Bundle);
method public abstract void onActivityStarted(android.app.Activity);
@@ -6095,6 +6113,11 @@
method public abstract void onSharedElementsReady();
}
+ public class SmsAppService extends android.app.Service {
+ ctor public SmsAppService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ }
+
public deprecated class TabActivity extends android.app.ActivityGroup {
ctor public TabActivity();
method public android.widget.TabHost getTabHost();
@@ -9527,6 +9550,7 @@
field public static final int BIND_IMPORTANT = 64; // 0x40
field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
+ field public static final java.lang.String BIOMETRIC_SERVICE = "biometric";
field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
field public static final java.lang.String CAMERA_SERVICE = "camera";
field public static final java.lang.String CAPTIONING_SERVICE = "captioning";
@@ -14006,6 +14030,10 @@
method public float getRunAdvance(char[], int, int, int, int, boolean, int);
method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
method public android.graphics.Shader getShader();
+ method public int getShadowLayerColor();
+ method public float getShadowLayerDx();
+ method public float getShadowLayerDy();
+ method public float getShadowLayerRadius();
method public float getStrikeThruPosition();
method public float getStrikeThruThickness();
method public android.graphics.Paint.Cap getStrokeCap();
@@ -15309,7 +15337,7 @@
method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
}
- public class SystemFonts {
+ public final class SystemFonts {
method public static java.util.Set<android.graphics.fonts.Font> getAvailableFonts();
}
@@ -15934,6 +15962,10 @@
package android.hardware.biometrics {
+ public class BiometricManager {
+ method public boolean hasEnrolledBiometrics();
+ }
+
public class BiometricPrompt {
method public void authenticate(android.hardware.biometrics.BiometricPrompt.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
@@ -18639,6 +18671,114 @@
method public java.lang.CharSequence getName();
}
+ public class Bidi {
+ ctor public Bidi();
+ ctor public Bidi(int, int);
+ ctor public Bidi(java.lang.String, int);
+ ctor public Bidi(java.text.AttributedCharacterIterator);
+ ctor public Bidi(char[], int, byte[], int, int, int);
+ method public boolean baseIsLeftToRight();
+ method public int countParagraphs();
+ method public int countRuns();
+ method public android.icu.text.Bidi createLineBidi(int, int);
+ method public static byte getBaseDirection(java.lang.CharSequence);
+ method public int getBaseLevel();
+ method public android.icu.text.BidiClassifier getCustomClassifier();
+ method public int getCustomizedClass(int);
+ method public byte getDirection();
+ method public int getLength();
+ method public byte getLevelAt(int);
+ method public byte[] getLevels();
+ method public int getLogicalIndex(int);
+ method public int[] getLogicalMap();
+ method public android.icu.text.BidiRun getLogicalRun(int);
+ method public byte getParaLevel();
+ method public android.icu.text.BidiRun getParagraph(int);
+ method public android.icu.text.BidiRun getParagraphByIndex(int);
+ method public int getParagraphIndex(int);
+ method public int getProcessedLength();
+ method public int getReorderingMode();
+ method public int getReorderingOptions();
+ method public int getResultLength();
+ method public int getRunCount();
+ method public int getRunLevel(int);
+ method public int getRunLimit(int);
+ method public int getRunStart(int);
+ method public char[] getText();
+ method public java.lang.String getTextAsString();
+ method public int getVisualIndex(int);
+ method public int[] getVisualMap();
+ method public android.icu.text.BidiRun getVisualRun(int);
+ method public static int[] invertMap(int[]);
+ method public boolean isInverse();
+ method public boolean isLeftToRight();
+ method public boolean isMixed();
+ method public boolean isOrderParagraphsLTR();
+ method public boolean isRightToLeft();
+ method public void orderParagraphsLTR(boolean);
+ method public static int[] reorderLogical(byte[]);
+ method public static int[] reorderVisual(byte[]);
+ method public static void reorderVisually(byte[], int, java.lang.Object[], int, int);
+ method public static boolean requiresBidi(char[], int, int);
+ method public void setContext(java.lang.String, java.lang.String);
+ method public void setCustomClassifier(android.icu.text.BidiClassifier);
+ method public void setInverse(boolean);
+ method public android.icu.text.Bidi setLine(int, int);
+ method public void setPara(java.lang.String, byte, byte[]);
+ method public void setPara(char[], byte, byte[]);
+ method public void setPara(java.text.AttributedCharacterIterator);
+ method public void setReorderingMode(int);
+ method public void setReorderingOptions(int);
+ method public java.lang.String writeReordered(int);
+ method public static java.lang.String writeReverse(java.lang.String, int);
+ field public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = 126; // 0x7e
+ field public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = 127; // 0x7f
+ field public static final int DIRECTION_LEFT_TO_RIGHT = 0; // 0x0
+ field public static final int DIRECTION_RIGHT_TO_LEFT = 1; // 0x1
+ field public static final short DO_MIRRORING = 2; // 0x2
+ field public static final short INSERT_LRM_FOR_NUMERIC = 4; // 0x4
+ field public static final short KEEP_BASE_COMBINING = 1; // 0x1
+ field public static final byte LEVEL_DEFAULT_LTR = 126; // 0x7e
+ field public static final byte LEVEL_DEFAULT_RTL = 127; // 0x7f
+ field public static final byte LEVEL_OVERRIDE = -128; // 0xffffff80
+ field public static final byte LTR = 0; // 0x0
+ field public static final int MAP_NOWHERE = -1; // 0xffffffff
+ field public static final byte MAX_EXPLICIT_LEVEL = 125; // 0x7d
+ field public static final byte MIXED = 2; // 0x2
+ field public static final byte NEUTRAL = 3; // 0x3
+ field public static final int OPTION_DEFAULT = 0; // 0x0
+ field public static final int OPTION_INSERT_MARKS = 1; // 0x1
+ field public static final int OPTION_REMOVE_CONTROLS = 2; // 0x2
+ field public static final int OPTION_STREAMING = 4; // 0x4
+ field public static final short OUTPUT_REVERSE = 16; // 0x10
+ field public static final short REMOVE_BIDI_CONTROLS = 8; // 0x8
+ field public static final short REORDER_DEFAULT = 0; // 0x0
+ field public static final short REORDER_GROUP_NUMBERS_WITH_R = 2; // 0x2
+ field public static final short REORDER_INVERSE_FOR_NUMBERS_SPECIAL = 6; // 0x6
+ field public static final short REORDER_INVERSE_LIKE_DIRECT = 5; // 0x5
+ field public static final short REORDER_INVERSE_NUMBERS_AS_L = 4; // 0x4
+ field public static final short REORDER_NUMBERS_SPECIAL = 1; // 0x1
+ field public static final short REORDER_RUNS_ONLY = 3; // 0x3
+ field public static final byte RTL = 1; // 0x1
+ }
+
+ public class BidiClassifier {
+ ctor public BidiClassifier(java.lang.Object);
+ method public int classify(int);
+ method public java.lang.Object getContext();
+ method public void setContext(java.lang.Object);
+ }
+
+ public class BidiRun {
+ method public byte getDirection();
+ method public byte getEmbeddingLevel();
+ method public int getLength();
+ method public int getLimit();
+ method public int getStart();
+ method public boolean isEvenRun();
+ method public boolean isOddRun();
+ }
+
public abstract class BreakIterator implements java.lang.Cloneable {
ctor protected BreakIterator();
method public java.lang.Object clone();
@@ -21510,7 +21650,7 @@
method public android.view.inputmethod.InputConnection getCurrentInputConnection();
method public android.view.inputmethod.EditorInfo getCurrentInputEditorInfo();
method public boolean getCurrentInputStarted();
- method public int getInputMethodWindowRecommendedHeight();
+ method public deprecated int getInputMethodWindowRecommendedHeight();
method public android.view.LayoutInflater getLayoutInflater();
method public int getMaxWidth();
method public java.lang.CharSequence getTextForImeAction(int);
@@ -27145,6 +27285,7 @@
method public android.net.Network[] getAllNetworks();
method public deprecated boolean getBackgroundDataSetting();
method public android.net.Network getBoundNetworkForProcess();
+ method public int getConnectionOwnerUid(int, java.net.InetSocketAddress, java.net.InetSocketAddress);
method public android.net.ProxyInfo getDefaultProxy();
method public android.net.LinkProperties getLinkProperties(android.net.Network);
method public int getMultipathPreference(android.net.Network);
@@ -32382,6 +32523,7 @@
public class Build {
ctor public Build();
+ method public static java.util.List<android.os.Build.Partition> getPartitions();
method public static java.lang.String getRadioVersion();
method public static java.lang.String getSerial();
field public static final java.lang.String BOARD;
@@ -32410,6 +32552,14 @@
field public static final java.lang.String USER;
}
+ public static class Build.Partition {
+ ctor public Build.Partition();
+ method public java.lang.String getFingerprint();
+ method public java.lang.String getName();
+ method public long getTimeMillis();
+ field public static final java.lang.String PARTITION_NAME_SYSTEM = "system";
+ }
+
public static class Build.VERSION {
ctor public Build.VERSION();
field public static final java.lang.String BASE_OS;
@@ -33292,6 +33442,7 @@
method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final deprecated boolean supportsProcesses();
field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
+ field public static final int INVALID_UID = -1; // 0xffffffff
field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
field public static final int PHONE_UID = 1001; // 0x3e9
field public static final int SIGNAL_KILL = 9; // 0x9
@@ -33583,6 +33734,7 @@
field public static final java.lang.String DISALLOW_FUN = "no_fun";
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_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";
@@ -39928,6 +40080,7 @@
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
+ method public java.util.Set<java.lang.String> onGetSupportedVoiceActions(java.util.Set<java.lang.String>);
method public void onLaunchVoiceAssistFromKeyguard();
method public void onReady();
method public void onShutdown();
@@ -39990,6 +40143,7 @@
field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
+ field public static final int SHOW_SOURCE_NOTIFICATION = 64; // 0x40
field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20
field public static final int SHOW_WITH_ASSIST = 1; // 0x1
field public static final int SHOW_WITH_SCREENSHOT = 2; // 0x2
@@ -41195,6 +41349,7 @@
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+ field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
field public static final int PROPERTY_RTT = 1024; // 0x400
field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
field public static final int PROPERTY_WIFI = 8; // 0x8
@@ -41850,6 +42005,7 @@
field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+ field public static final java.lang.String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE";
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
@@ -42147,6 +42303,7 @@
field public static final deprecated java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
field public static final java.lang.String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+ field public static final java.lang.String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
@@ -42408,6 +42565,13 @@
field public static final int STATUS_UNKNOWN = 0; // 0x0
}
+ 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, 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);
+ }
+
public class MbmsStreamingSession implements java.lang.AutoCloseable {
method public void close();
method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
@@ -42416,7 +42580,7 @@
method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
- public class NeighboringCellInfo implements android.os.Parcelable {
+ public deprecated class NeighboringCellInfo implements android.os.Parcelable {
ctor public deprecated NeighboringCellInfo();
ctor public deprecated NeighboringCellInfo(int, int);
ctor public NeighboringCellInfo(int, java.lang.String, int);
@@ -42786,8 +42950,10 @@
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+ method public static int getSlotIndex(int);
method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+ method public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
method public static boolean isValidSubscriptionId(int);
method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
@@ -42803,7 +42969,9 @@
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+ field public static final int INVALID_SIM_SLOT_INDEX = -2; // 0xfffffffe
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
+ field public static final int SIM_NOT_INSERTED = -3; // 0xfffffffd
}
public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
@@ -42873,7 +43041,6 @@
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
method public java.lang.String getNai();
- method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
method public java.lang.String getNetworkOperator();
method public java.lang.String getNetworkOperatorName();
@@ -42938,6 +43105,7 @@
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final java.lang.String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+ field public static final java.lang.String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE";
field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
@@ -43354,6 +43522,29 @@
field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
}
+ public class GroupCall implements java.lang.AutoCloseable {
+ method public void close();
+ method public long getTmgi();
+ method public void updateGroupCall(int[], int[]);
+ 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
+ field public static final int REASON_NONE = 0; // 0x0
+ field public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; // 0x5
+ field public static final int REASON_OUT_OF_MEMORY = 4; // 0x4
+ field public static final int STATE_STALLED = 3; // 0x3
+ field public static final int STATE_STARTED = 2; // 0x2
+ 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);
+ field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
+ }
+
public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
ctor public MbmsDownloadReceiver();
method public void onReceive(android.content.Context, android.content.Intent);
@@ -43402,6 +43593,14 @@
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 class MbmsStreamingSessionCallback {
ctor public MbmsStreamingSessionCallback();
method public void onError(int, java.lang.String);
@@ -44036,6 +44235,8 @@
field public float density;
field public int[] drawableState;
field public int linkColor;
+ field public int underlineColor;
+ field public float underlineThickness;
}
public class TextUtils {
@@ -44850,11 +45051,20 @@
ctor public TextAppearanceSpan(android.os.Parcel);
method public int describeContents();
method public java.lang.String getFamily();
+ method public java.lang.String getFontFeatureSettings();
+ method public java.lang.String getFontVariationSettings();
method public android.content.res.ColorStateList getLinkTextColor();
+ method public int getShadowColor();
+ method public float getShadowDx();
+ method public float getShadowDy();
+ method public float getShadowRadius();
method public int getSpanTypeId();
method public android.content.res.ColorStateList getTextColor();
+ method public int getTextFontWeight();
method public int getTextSize();
method public int getTextStyle();
+ method public android.graphics.Typeface getTypeface();
+ method public boolean isElegantTextHeight();
method public void updateDrawState(android.text.TextPaint);
method public void updateMeasureState(android.text.TextPaint);
method public void writeToParcel(android.os.Parcel, int);
@@ -45497,6 +45707,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public V get(java.lang.Object);
method public int indexOfKey(java.lang.Object);
+ method public int indexOfValue(java.lang.Object);
method public boolean isEmpty();
method public K keyAt(int);
method public java.util.Set<K> keySet();
@@ -45517,6 +45728,7 @@
ctor public ArraySet();
ctor public ArraySet(int);
ctor public ArraySet(android.util.ArraySet<E>);
+ ctor public ArraySet(java.util.Collection<? extends E>);
method public boolean add(E);
method public void addAll(android.util.ArraySet<? extends E>);
method public boolean addAll(java.util.Collection<? extends E>);
@@ -46088,6 +46300,7 @@
method public int keyAt(int);
method public void put(int, boolean);
method public void removeAt(int);
+ method public void setValueAt(int, boolean);
method public int size();
method public boolean valueAt(int);
}
@@ -46106,6 +46319,7 @@
method public int keyAt(int);
method public void put(int, int);
method public void removeAt(int);
+ method public void setValueAt(int, int);
method public int size();
method public int valueAt(int);
}
@@ -50123,7 +50337,7 @@
method public long computeDurationHint();
method protected void ensureInterpolator();
method public int getBackgroundColor();
- method public boolean getDetachWallpaper();
+ method public deprecated boolean getDetachWallpaper();
method public long getDuration();
method public boolean getFillAfter();
method public boolean getFillBefore();
@@ -50147,7 +50361,7 @@
method public void scaleCurrentDuration(float);
method public void setAnimationListener(android.view.animation.Animation.AnimationListener);
method public void setBackgroundColor(int);
- method public void setDetachWallpaper(boolean);
+ method public deprecated void setDetachWallpaper(boolean);
method public void setDuration(long);
method public void setFillAfter(boolean);
method public void setFillBefore(boolean);
@@ -51240,7 +51454,7 @@
}
public abstract class CookieManager {
- ctor public CookieManager();
+ ctor public deprecated CookieManager();
method public abstract boolean acceptCookie();
method public abstract boolean acceptThirdPartyCookies(android.webkit.WebView);
method public static boolean allowFileSchemeCookies();
@@ -51340,13 +51554,13 @@
}
public abstract class RenderProcessGoneDetail {
- ctor public RenderProcessGoneDetail();
+ ctor public deprecated RenderProcessGoneDetail();
method public abstract boolean didCrash();
method public abstract int rendererPriorityAtExit();
}
public abstract class SafeBrowsingResponse {
- ctor public SafeBrowsingResponse();
+ ctor public deprecated SafeBrowsingResponse();
method public abstract void backToSafety(boolean);
method public abstract void proceed(boolean);
method public abstract void showInterstitial(boolean);
@@ -51358,7 +51572,7 @@
}
public abstract class ServiceWorkerController {
- ctor public ServiceWorkerController();
+ ctor public deprecated ServiceWorkerController();
method public static android.webkit.ServiceWorkerController getInstance();
method public abstract android.webkit.ServiceWorkerWebSettings getServiceWorkerWebSettings();
method public abstract void setServiceWorkerClient(android.webkit.ServiceWorkerClient);
@@ -51407,7 +51621,7 @@
}
public abstract class TracingController {
- ctor public TracingController();
+ ctor public deprecated TracingController();
method public static android.webkit.TracingController getInstance();
method public abstract boolean isTracing();
method public abstract void start(android.webkit.TracingConfig);
@@ -51947,7 +52161,7 @@
}
public abstract class WebViewDatabase {
- ctor public WebViewDatabase();
+ ctor public deprecated WebViewDatabase();
method public abstract deprecated void clearFormData();
method public abstract void clearHttpAuthUsernamePassword();
method public abstract deprecated void clearUsernamePassword();
@@ -53227,7 +53441,7 @@
}
public final class Magnifier {
- ctor public Magnifier(android.view.View);
+ ctor public deprecated Magnifier(android.view.View);
method public void dismiss();
method public float getCornerRadius();
method public int getDefaultHorizontalSourceToMagnifierOffset();
@@ -55055,6 +55269,7 @@
public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
ctor public DelegateLastClassLoader(java.lang.String, java.lang.ClassLoader);
ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
+ ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader, boolean);
}
public class DexClassLoader extends dalvik.system.BaseDexClassLoader {
diff --git a/api/removed.txt b/api/removed.txt
index b6dabcd..f7106d2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -545,6 +545,7 @@
}
public class TelephonyManager {
+ method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 8f7606a..041b497 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -128,6 +128,7 @@
field public static final java.lang.String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
field public static final java.lang.String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
+ field public static final java.lang.String POWER_SAVER = "android.permission.POWER_SAVER";
field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
@@ -950,6 +951,7 @@
field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score";
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
+ field public static final java.lang.String PERMISSION_SERVICE = "permission";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final java.lang.String STATS_MANAGER = "stats";
@@ -3994,6 +3996,7 @@
}
public final class PowerManager {
+ method public boolean setPowerSaveMode(boolean);
method public void userActivity(long, int, int);
field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
@@ -4192,6 +4195,20 @@
}
+package android.permission {
+
+ public final class PermissionManager {
+ method public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+ }
+
+ public static final class PermissionManager.SplitPermissionInfo {
+ method public java.lang.String[] getNewPermissions();
+ method public java.lang.String getRootPermission();
+ method public int getTargetSdk();
+ }
+
+}
+
package android.permissionpresenterservice {
public abstract class RuntimePermissionPresenterService extends android.app.Service {
@@ -5223,6 +5240,7 @@
method public deprecated android.content.ComponentName getDefaultPhoneApp();
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
+ method public boolean isInEmergencyCall();
method public boolean isRinging();
method public boolean isTtySupported();
field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
@@ -5249,6 +5267,10 @@
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
}
+ public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+ field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall";
+ }
+
public class MbmsStreamingSession implements java.lang.AutoCloseable {
field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
@@ -5412,6 +5434,7 @@
method public boolean disableDataConnectivity();
method public boolean enableDataConnectivity();
method public void enableVideoCalling(boolean);
+ method public java.lang.String getAidForAppType(int);
method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -5426,6 +5449,8 @@
method public deprecated boolean getDataEnabled();
method public deprecated boolean getDataEnabled(int);
method public boolean getEmergencyCallbackMode();
+ method public java.lang.String getIsimDomain();
+ method public int getPreferredNetworkType(int);
method public int getSimApplicationState();
method public int getSimCardState();
method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -5435,10 +5460,7 @@
method public boolean handlePinMmi(java.lang.String);
method public boolean handlePinMmiForSubscriber(int, java.lang.String);
method public boolean isDataConnectivityPossible();
- method public deprecated boolean isIdle();
- method public deprecated boolean isOffhook();
method public deprecated boolean isRadioOn();
- method public deprecated boolean isRinging();
method public boolean isVideoCallingEnabled();
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
@@ -5452,7 +5474,6 @@
method public void setSimPowerStateForSlot(int, int);
method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
method public void setVoiceActivationState(int);
- method public deprecated void silenceRinger();
method public boolean supplyPin(java.lang.String);
method public int[] supplyPinReportResult(java.lang.String);
method public boolean supplyPuk(java.lang.String, java.lang.String);
@@ -5470,6 +5491,29 @@
field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+ field public static final int 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
+ field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7
+ field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1
+ field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3
+ field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8
+ field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa
+ field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9
+ field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb
+ field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf
+ field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16
+ field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11
+ field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14
+ field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13
+ field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc
+ field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15
+ field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10
+ field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12
+ field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd
+ field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
+ field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
+ field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
@@ -5805,6 +5849,7 @@
field public static final java.lang.String EXTRA_CODEC = "Codec";
field public static final java.lang.String EXTRA_DIALSTRING = "dialstring";
field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText";
+ field public static final java.lang.String EXTRA_E_CALL = "e_call";
field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull";
field public static final java.lang.String EXTRA_OI = "oi";
field public static final java.lang.String EXTRA_OIR = "oir";
@@ -6549,6 +6594,17 @@
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
}
+ public class MbmsGroupCallServiceBase extends android.app.Service {
+ ctor public MbmsGroupCallServiceBase();
+ method public void dispose(int) throws android.os.RemoteException;
+ 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 void stopGroupCall(int, long);
+ method public void updateGroupCall(int, long, int[], int[]);
+ }
+
public class MbmsStreamingServiceBase extends android.os.Binder {
ctor public MbmsStreamingServiceBase();
method public void dispose(int) throws android.os.RemoteException;
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 9012c33..2246562 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -148,6 +148,10 @@
public class TelephonyManager {
method public deprecated void answerRingingCall();
method public deprecated boolean endCall();
+ method public deprecated boolean isIdle();
+ method public deprecated boolean isOffhook();
+ method public deprecated boolean isRinging();
+ method public deprecated void silenceRinger();
}
}
diff --git a/api/test-current.txt b/api/test-current.txt
index e22f516..dd02504 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -708,6 +708,10 @@
method public void removeSyncBarrier(int);
}
+ public final class PowerManager {
+ method public boolean setPowerSaveMode(boolean);
+ }
+
public class Process {
method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
}
@@ -1181,6 +1185,10 @@
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
}
+ public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+ field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA = "mbms-group-call-service-override";
+ }
+
public class MbmsStreamingSession implements java.lang.AutoCloseable {
field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
@@ -1250,6 +1258,17 @@
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
}
+ public class MbmsGroupCallServiceBase extends android.app.Service {
+ ctor public MbmsGroupCallServiceBase();
+ method public void dispose(int) throws android.os.RemoteException;
+ 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 void stopGroupCall(int, long);
+ method public void updateGroupCall(int, long, int[], int[]);
+ }
+
public class MbmsStreamingServiceBase extends android.os.Binder {
ctor public MbmsStreamingServiceBase();
method public void dispose(int) throws android.os.RemoteException;
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 36e51b9..12fb4a3 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -470,7 +470,8 @@
onExecute(provider);
} finally {
if (provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
+ activityManager.removeContentProviderExternalAsUser(
+ providerName, token, mUserId);
}
}
} catch (Exception e) {
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index e14f2eb..d334f96 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -198,8 +198,7 @@
sp<GraphicBuffer> outBuffer;
status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
- 0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
- &outBuffer);
+ 0 /* reqHeight */, false, captureOrientation, &outBuffer);
if (result != NO_ERROR) {
close(fd);
return 1;
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5e87ef6..f6b0db8 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -67,7 +67,9 @@
src/subscriber/SubscriberReporter.cpp \
src/HashableDimensionKey.cpp \
src/guardrail/StatsdStats.cpp \
- src/socket/StatsSocketListener.cpp
+ src/socket/StatsSocketListener.cpp \
+ src/shell/ShellSubscriber.cpp \
+ src/shell/shell_config.proto
# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
statsd_common_src += \
@@ -97,6 +99,7 @@
libhidlbase \
libhidltransport \
libhwbinder \
+ android.frameworks.stats@1.0 \
android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
@@ -144,10 +147,10 @@
LOCAL_MODULE_CLASS := EXECUTABLES
# Enable sanitizer ONLY on eng builds.
-ifeq ($(TARGET_BUILD_VARIANT),eng)
- LOCAL_CLANG := true
- LOCAL_SANITIZE := address
-endif
+#ifeq ($(TARGET_BUILD_VARIANT),eng)
+# LOCAL_CLANG := true
+# LOCAL_SANITIZE := address
+#endif
# Add a flag to enable stats log printing from statsd on debug builds.
ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
@@ -225,7 +228,8 @@
tests/e2e/Anomaly_count_e2e_test.cpp \
tests/e2e/Anomaly_duration_sum_e2e_test.cpp \
tests/e2e/ConfigTtl_e2e_test.cpp \
- tests/e2e/PartialBucket_e2e_test.cpp
+ tests/e2e/PartialBucket_e2e_test.cpp \
+ tests/shell/ShellSubscriber_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 8e02f9c..f4c70be 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -257,12 +257,18 @@
return it->second->byteSize();
}
-void StatsLogProcessor::dumpStates(FILE* out, bool verbose) {
+void StatsLogProcessor::dumpStates(int out, bool verbose) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- fprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
- for (auto metricsManager : mMetricsManagers) {
- metricsManager.second->dumpStates(out, verbose);
+ FILE* fout = fdopen(out, "w");
+ if (fout == NULL) {
+ return;
}
+ fprintf(fout, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
+ for (auto metricsManager : mMetricsManagers) {
+ metricsManager.second->dumpStates(fout, verbose);
+ }
+
+ fclose(fout);
}
/*
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index df80b8e..4091d95 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -83,7 +83,7 @@
return mUidMap;
}
- void dumpStates(FILE* out, bool verbose);
+ void dumpStates(int outFd, bool verbose);
void informPullAlarmFired(const int64_t timestampNs);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 9a79345..8da2d44 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -34,13 +34,14 @@
#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
-#include <utils/Looper.h>
-#include <utils/String16.h>
#include <statslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
#include <unistd.h>
+#include <utils/Looper.h>
+#include <utils/String16.h>
+#include <chrono>
using namespace android;
@@ -214,30 +215,8 @@
sp<IResultReceiver> resultReceiver =
IResultReceiver::asInterface(data.readStrongBinder());
- FILE* fin = fdopen(in, "r");
- FILE* fout = fdopen(out, "w");
- FILE* ferr = fdopen(err, "w");
-
- if (fin == NULL || fout == NULL || ferr == NULL) {
- resultReceiver->send(NO_MEMORY);
- } else {
- err = command(fin, fout, ferr, args);
- resultReceiver->send(err);
- }
-
- if (fin != NULL) {
- fflush(fin);
- fclose(fin);
- }
- if (fout != NULL) {
- fflush(fout);
- fclose(fout);
- }
- if (fout != NULL) {
- fflush(ferr);
- fclose(ferr);
- }
-
+ err = command(in, out, err, args, resultReceiver);
+ resultReceiver->send(err);
return NO_ERROR;
}
default: { return BnStatsManager::onTransact(code, data, reply, flags); }
@@ -251,10 +230,6 @@
if (!checkCallingPermission(String16(kPermissionDump))) {
return PERMISSION_DENIED;
}
- FILE* out = fdopen(fd, "w");
- if (out == NULL) {
- return NO_MEMORY; // the fd is already open
- }
bool verbose = false;
bool proto = false;
@@ -265,21 +240,20 @@
proto = true;
}
- dump_impl(out, verbose, proto);
+ dump_impl(fd, verbose, proto);
- fclose(out);
return NO_ERROR;
}
/**
* Write debugging data about statsd in text or proto format.
*/
-void StatsService::dump_impl(FILE* out, bool verbose, bool proto) {
+void StatsService::dump_impl(int out, bool verbose, bool proto) {
if (proto) {
vector<uint8_t> data;
StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats.
for (size_t i = 0; i < data.size(); i ++) {
- fprintf(out, "%c", data[i]);
+ dprintf(out, "%c", data[i]);
}
} else {
StatsdStats::getInstance().dumpStats(out);
@@ -290,7 +264,8 @@
/**
* Implementation of the adb shell cmd stats command.
*/
-status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
+ sp<IResultReceiver> resultReceiver) {
uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid != AID_ROOT && uid != AID_SHELL) {
return PERMISSION_DENIED;
@@ -342,97 +317,106 @@
if (!args[0].compare(String8("print-logs"))) {
return cmd_print_logs(out, args);
}
+ if (!args[0].compare(String8("data-subscribe"))) {
+ if (mShellSubscriber == nullptr) {
+ mShellSubscriber = new ShellSubscriber(mUidMap);
+ }
+ mShellSubscriber->startNewSubscription(in, out, resultReceiver);
+ return NO_ERROR;
+ }
}
print_cmd_help(out);
return NO_ERROR;
}
-void StatsService::print_cmd_help(FILE* out) {
- fprintf(out,
+void StatsService::print_cmd_help(int out) {
+ dprintf(out,
"usage: adb shell cmd stats print-stats-log [tag_required] "
"[timestamp_nsec_optional]\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats meminfo\n");
- fprintf(out, "\n");
- fprintf(out, " Prints the malloc debug information. You need to run the following first: \n");
- fprintf(out, " # adb shell stop\n");
- fprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n");
- fprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n");
- fprintf(out, " # adb shell start\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n");
- fprintf(out, "\n");
- fprintf(out, " Prints the UID, app name, version mapping.\n");
- fprintf(out, " PKG Optional package name to print the uids of the package\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
- fprintf(out, "\n");
- fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats write-to-disk \n");
- fprintf(out, "\n");
- fprintf(out, " Flushes all data on memory to disk.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n");
- fprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n");
- fprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
- fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
- fprintf(out, " uid is used.\n");
- fprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
- fprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
- fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
- fprintf(out, "\n");
- fprintf(out, " Adds, updates or removes a configuration. The proto should be in\n");
- fprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n");
- fprintf(out, " provided, then all configs will be removed from memory and disk.\n");
- fprintf(out, "\n");
- fprintf(out, " UID The uid to use. It is only possible to pass the UID\n");
- fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
- fprintf(out, " uid is used.\n");
- fprintf(out, " NAME The per-uid name to use\n");
- fprintf(out, "\n");
- fprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n");
- fprintf(out, "\n be removed from memory and disk!\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] [--proto]\n");
- fprintf(out, " Dump all metric data for a configuration.\n");
- fprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
- fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
- fprintf(out, " calling uid is used.\n");
- fprintf(out, " NAME The name of the configuration\n");
- fprintf(out, " --proto Print proto binary.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
- fprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n");
- fprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
- fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
- fprintf(out, " calling uid is used.\n");
- fprintf(out, " NAME The name of the configuration\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-stats\n");
- fprintf(out, " Prints some basic stats.\n");
- fprintf(out, " --proto Print proto binary instead of string format.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
- fprintf(out, " Clear cached puller data.\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-logs\n");
- fprintf(out, " Only works on eng build\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats meminfo\n");
+ dprintf(out, "\n");
+ dprintf(out, " Prints the malloc debug information. You need to run the following first: \n");
+ dprintf(out, " # adb shell stop\n");
+ dprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n");
+ dprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n");
+ dprintf(out, " # adb shell start\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n");
+ dprintf(out, "\n");
+ dprintf(out, " Prints the UID, app name, version mapping.\n");
+ dprintf(out, " PKG Optional package name to print the uids of the package\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
+ dprintf(out, "\n");
+ dprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats write-to-disk \n");
+ dprintf(out, "\n");
+ dprintf(out, " Flushes all data on memory to disk.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n");
+ dprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n");
+ dprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
+ dprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
+ dprintf(out, " uid is used.\n");
+ dprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
+ dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
+ dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
+ dprintf(out, "\n");
+ dprintf(out, " Adds, updates or removes a configuration. The proto should be in\n");
+ dprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n");
+ dprintf(out, " provided, then all configs will be removed from memory and disk.\n");
+ dprintf(out, "\n");
+ dprintf(out, " UID The uid to use. It is only possible to pass the UID\n");
+ dprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
+ dprintf(out, " uid is used.\n");
+ dprintf(out, " NAME The per-uid name to use\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n");
+ dprintf(out, "\n be removed from memory and disk!\n");
+ dprintf(out, "\n");
+ dprintf(out,
+ "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] "
+ "[--proto]\n");
+ dprintf(out, " Dump all metric data for a configuration.\n");
+ dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ dprintf(out, " calling uid is used.\n");
+ dprintf(out, " NAME The name of the configuration\n");
+ dprintf(out, " --proto Print proto binary.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
+ dprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n");
+ dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ dprintf(out, " calling uid is used.\n");
+ dprintf(out, " NAME The name of the configuration\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats print-stats\n");
+ dprintf(out, " Prints some basic stats.\n");
+ dprintf(out, " --proto Print proto binary instead of string format.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
+ dprintf(out, " Clear cached puller data.\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats print-logs\n");
+ dprintf(out, " Only works on eng build\n");
}
-status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
+status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
string name;
bool good = false;
int uid;
@@ -456,9 +440,9 @@
}
}
} else {
- fprintf(out,
+ dprintf(out,
"The metrics can only be dumped for other UIDs on eng or userdebug "
- "builds.\n");
+ "builds.\n");
}
}
if (!good) {
@@ -481,7 +465,7 @@
return NO_ERROR;
}
-status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) {
const int argCount = args.size();
if (argCount >= 2) {
if (args[1] == "update" || args[1] == "remove") {
@@ -508,7 +492,7 @@
}
}
} else {
- fprintf(err,
+ dprintf(err,
"The config can only be set for other UIDs on eng or userdebug "
"builds.\n");
}
@@ -526,21 +510,21 @@
char* endp;
int64_t configID = strtoll(name.c_str(), &endp, 10);
if (endp == name.c_str() || *endp != '\0') {
- fprintf(err, "Error parsing config ID.\n");
+ dprintf(err, "Error parsing config ID.\n");
return UNKNOWN_ERROR;
}
// Read stream into buffer.
string buffer;
- if (!android::base::ReadFdToString(fileno(in), &buffer)) {
- fprintf(err, "Error reading stream for StatsConfig.\n");
+ if (!android::base::ReadFdToString(in, &buffer)) {
+ dprintf(err, "Error reading stream for StatsConfig.\n");
return UNKNOWN_ERROR;
}
// Parse buffer.
StatsdConfig config;
if (!config.ParseFromString(buffer)) {
- fprintf(err, "Error parsing proto stream for StatsConfig.\n");
+ dprintf(err, "Error parsing proto stream for StatsConfig.\n");
return UNKNOWN_ERROR;
}
@@ -562,7 +546,7 @@
return UNKNOWN_ERROR;
}
-status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String8>& args) {
+status_t StatsService::cmd_dump_report(int out, int err, const Vector<String8>& args) {
if (mProcessor != nullptr) {
int argCount = args.size();
bool good = false;
@@ -597,7 +581,7 @@
}
}
} else {
- fprintf(out,
+ dprintf(out,
"The metrics can only be dumped for other UIDs on eng or userdebug "
"builds.\n");
}
@@ -608,11 +592,11 @@
includeCurrentBucket, ADB_DUMP, &data);
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
- fprintf(out, "%c", data[i]);
+ dprintf(out, "%c", data[i]);
}
} else {
- fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
- fprintf(out, "See the StatsLogReport in logcat...\n");
+ dprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
+ dprintf(out, "See the StatsLogReport in logcat...\n");
}
return android::OK;
} else {
@@ -621,12 +605,12 @@
return UNKNOWN_ERROR;
}
} else {
- fprintf(out, "Log processor does not exist...\n");
+ dprintf(out, "Log processor does not exist...\n");
return UNKNOWN_ERROR;
}
}
-status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_stats(int out, const Vector<String8>& args) {
int argCount = args.size();
bool proto = false;
if (!std::strcmp("--proto", args[argCount-1].c_str())) {
@@ -638,13 +622,13 @@
vector<uint8_t> data;
statsdStats.dumpStats(&data, false); // does not reset statsdStats.
for (size_t i = 0; i < data.size(); i ++) {
- fprintf(out, "%c", data[i]);
+ dprintf(out, "%c", data[i]);
}
} else {
vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
for (const ConfigKey& key : configs) {
- fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+ dprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
mProcessor->GetMetricsSize(key));
}
statsdStats.dumpStats(out);
@@ -652,29 +636,29 @@
return NO_ERROR;
}
-status_t StatsService::cmd_print_uid_map(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_uid_map(int out, const Vector<String8>& args) {
if (args.size() > 1) {
string pkg;
pkg.assign(args[1].c_str(), args[1].size());
auto uids = mUidMap->getAppUid(pkg);
- fprintf(out, "%s -> [ ", pkg.c_str());
+ dprintf(out, "%s -> [ ", pkg.c_str());
for (const auto& uid : uids) {
- fprintf(out, "%d ", uid);
+ dprintf(out, "%d ", uid);
}
- fprintf(out, "]\n");
+ dprintf(out, "]\n");
} else {
mUidMap->printUidMap(out);
}
return NO_ERROR;
}
-status_t StatsService::cmd_write_data_to_disk(FILE* out) {
- fprintf(out, "Writing data to disk\n");
+status_t StatsService::cmd_write_data_to_disk(int out) {
+ dprintf(out, "Writing data to disk\n");
mProcessor->WriteDataToDisk(ADB_DUMP);
return NO_ERROR;
}
-status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& args) {
bool good = false;
int32_t uid;
int32_t label;
@@ -695,13 +679,13 @@
state = atoi(args[3].c_str());
good = true;
} else {
- fprintf(out,
+ dprintf(out,
"Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
- "on eng or userdebug builds.\n");
+ "on eng or userdebug builds.\n");
}
}
if (good) {
- fprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
+ dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state);
} else {
print_cmd_help(out);
@@ -710,46 +694,46 @@
return NO_ERROR;
}
-status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
- fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
+ dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
- fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
+ dprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
return NO_ERROR;
}
return UNKNOWN_ERROR;
}
-status_t StatsService::cmd_remove_all_configs(FILE* out) {
- fprintf(out, "Removing all configs...\n");
+status_t StatsService::cmd_remove_all_configs(int out) {
+ dprintf(out, "Removing all configs...\n");
VLOG("StatsService::cmd_remove_all_configs was called");
mConfigManager->RemoveAllConfigs();
StorageManager::deleteAllFiles(STATS_SERVICE_DIR);
return NO_ERROR;
}
-status_t StatsService::cmd_dump_memory_info(FILE* out) {
- fprintf(out, "meminfo not available.\n");
+status_t StatsService::cmd_dump_memory_info(int out) {
+ dprintf(out, "meminfo not available.\n");
return NO_ERROR;
}
-status_t StatsService::cmd_clear_puller_cache(FILE* out) {
+status_t StatsService::cmd_clear_puller_cache(int out) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
ipc->getCallingPid(), ipc->getCallingUid());
if (checkCallingPermission(String16(kPermissionDump))) {
int cleared = mPullerManager->ForceClearPullerCache();
- fprintf(out, "Puller removed %d cached data!\n", cleared);
+ dprintf(out, "Puller removed %d cached data!\n", cleared);
return NO_ERROR;
} else {
return PERMISSION_DENIED;
}
}
-status_t StatsService::cmd_print_logs(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
ipc->getCallingUid());
@@ -885,6 +869,9 @@
void StatsService::OnLogEvent(LogEvent* event) {
mProcessor->OnLogEvent(event);
+ if (mShellSubscriber != nullptr) {
+ mShellSubscriber->onLogEvent(*event);
+ }
}
Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
@@ -1005,6 +992,60 @@
return Status::ok();
}
+hardware::Return<void> StatsService::reportSpeakerImpedance(
+ const SpeakerImpedance& speakerImpedance) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), hardwareFailed);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), physicalDropDetected);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), chargeCycles);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(),
+ batteryHealthSnapshotArgs);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), slowIo);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), batteryCausedShutdown);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
void StatsService::binderDied(const wp <IBinder>& who) {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 613f509..1f1d782 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,8 +24,11 @@
#include "external/StatsPullerManager.h"
#include "logd/LogListener.h"
#include "packages/UidMap.h"
+#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
+#include <android/frameworks/stats/1.0/IStats.h>
+#include <android/frameworks/stats/1.0/types.h>
#include <android/os/BnStatsManager.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
@@ -37,6 +40,7 @@
using namespace android;
using namespace android::base;
using namespace android::binder;
+using namespace android::frameworks::stats::V1_0;
using namespace android::os;
using namespace std;
@@ -44,7 +48,12 @@
namespace os {
namespace statsd {
-class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient {
+using android::hardware::Return;
+
+class StatsService : public BnStatsManager,
+ public LogListener,
+ public IStats,
+ public IBinder::DeathRecipient {
public:
StatsService(const sp<Looper>& handlerLooper);
virtual ~StatsService();
@@ -54,7 +63,8 @@
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
+ sp<IResultReceiver> resultReceiver);
virtual Status systemRunning();
virtual Status statsCompanionReady();
@@ -144,6 +154,44 @@
*/
virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
+ /**
+ * Binder call to get SpeakerImpedance atom.
+ */
+ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
+
+ /**
+ * Binder call to get HardwareFailed atom.
+ */
+ virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
+
+ /**
+ * Binder call to get PhysicalDropDetected atom.
+ */
+ virtual Return<void> reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) override;
+
+ /**
+ * Binder call to get ChargeCyclesReported atom.
+ */
+ virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+
+ /**
+ * Binder call to get BatteryHealthSnapshot atom.
+ */
+ virtual Return<void> reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
+
+ /**
+ * Binder call to get SlowIo atom.
+ */
+ virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
+
+ /**
+ * Binder call to get BatteryCausedShutdown atom.
+ */
+ virtual Return<void> reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) override;
+
/** IBinder::DeathRecipient */
virtual void binderDied(const wp<IBinder>& who) override;
@@ -162,73 +210,73 @@
/**
* Text or proto output of dumpsys.
*/
- void dump_impl(FILE* out, bool verbose, bool proto);
+ void dump_impl(int outFd, bool verbose, bool proto);
/**
* Print usage information for the commands
*/
- void print_cmd_help(FILE* out);
+ void print_cmd_help(int out);
/**
* Trigger a broadcast.
*/
- status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args);
+ status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args);
/**
* Handle the config sub-command.
*/
- status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ status_t cmd_config(int inFd, int outFd, int err, Vector<String8>& args);
/**
* Prints some basic stats to std out.
*/
- status_t cmd_print_stats(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_stats(int outFd, const Vector<String8>& args);
/**
* Print the event log.
*/
- status_t cmd_dump_report(FILE* out, FILE* err, const Vector<String8>& args);
+ status_t cmd_dump_report(int outFd, int err, const Vector<String8>& args);
/**
* Print the mapping of uids to package names.
*/
- status_t cmd_print_uid_map(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_uid_map(int outFd, const Vector<String8>& args);
/**
* Flush the data to disk.
*/
- status_t cmd_write_data_to_disk(FILE* out);
+ status_t cmd_write_data_to_disk(int outFd);
/**
* Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling
* StatsLog.write(APP_BREADCRUMB_REPORTED).
*/
- status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args);
+ status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args);
/**
* Print contents of a pulled metrics source.
*/
- status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args);
/**
* Removes all configs stored on disk and on memory.
*/
- status_t cmd_remove_all_configs(FILE* out);
+ status_t cmd_remove_all_configs(int outFd);
/*
* Dump memory usage by statsd.
*/
- status_t cmd_dump_memory_info(FILE* out);
+ status_t cmd_dump_memory_info(int outFd);
/*
* Clear all puller cached data
*/
- status_t cmd_clear_puller_cache(FILE* out);
+ status_t cmd_clear_puller_cache(int outFd);
/**
* Print all stats logs received to logcat.
*/
- status_t cmd_print_logs(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_logs(int outFd, const Vector<String8>& args);
/**
* Adds a configuration after checking permissions and obtaining UID from binder call.
@@ -275,6 +323,8 @@
*/
bool mEngBuild;
+ sp<ShellSubscriber> mShellSubscriber;
+
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b8f19ee..988ffc4 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -80,7 +80,8 @@
BatteryLevelChanged battery_level_changed = 30;
ChargingStateChanged charging_state_changed = 31;
PluggedStateChanged plugged_state_changed = 32;
- // 33 - 34 are available
+ InteractiveStateChanged interactive_state_changed = 33;
+ // 34 is available
WakeupAlarmOccurred wakeup_alarm_occurred = 35;
KernelWakeupReported kernel_wakeup_reported = 36;
WifiLockStateChanged wifi_lock_state_changed = 37;
@@ -136,6 +137,10 @@
FingerprintAcquired fingerprint_acquired = 87;
FingerprintAuthenticated fingerprint_authenticated = 88;
FingerprintErrorOccurred fingerprint_error_occurred = 89;
+ Notification notification = 90;
+ BatteryHealthSnapshot battery_health_snapshot = 91;
+ SlowIo slow_io = 92;
+ BatteryCausedShutdown battery_caused_shutdown = 93;
}
// Pulled events will start at field 10000.
@@ -173,10 +178,12 @@
BatteryVoltage battery_voltage = 10030;
NumFingerprints num_fingerprints = 10031;
ProcStats proc_stats = 10029;
+ DiskIo disk_io = 10032;
}
- // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
- // 100,000 are reserved for non-AOSP (e.g. OEMs) to use.
+ // DO NOT USE field numbers above 100,000 in AOSP.
+ // Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use.
+ // Field numbers 200,000 and above are reserved for future use; do not use them at all.
}
/**
@@ -197,9 +204,10 @@
message KeyValuePair {
optional int32 key = 1;
oneof value {
- int64 value_int = 2;
- string value_str = 3;
- float value_float = 4;
+ int32 value_int = 2;
+ int64 value_long = 3;
+ string value_str = 4;
+ float value_float = 5;
}
}
@@ -663,6 +671,20 @@
}
/**
+ * Logs when the device is interactive, according to the PowerManager Notifier.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/power/Notifier.java
+ */
+message InteractiveStateChanged {
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 1;
+}
+
+/**
* Logs Battery Saver state change.
*
* Logged from:
@@ -752,6 +774,9 @@
// Name of the wakeup alarm.
optional string tag = 2;
+
+ // Name of source package (for historical reasons, since BatteryStats tracked it).
+ optional string package_name = 3;
}
/**
@@ -1292,6 +1317,68 @@
}
/**
+ * Log battery health snapshot.
+ *
+ * Resistance, Voltage, Open Circuit Voltage, Temperature, and Charge Level
+ * are snapshotted periodically over 24hrs.
+ */
+message BatteryHealthSnapshot {
+ enum BatterySnapshotType {
+ UNKNOWN = 0;
+ MIN_TEMP = 1; // Snapshot at min batt temp over 24hrs.
+ MAX_TEMP = 2; // Snapshot at max batt temp over 24hrs.
+ MIN_RESISTANCE = 3; // Snapshot at min batt resistance over 24hrs.
+ MAX_RESISTANCE = 4; // Snapshot at max batt resistance over 24hrs.
+ MIN_VOLTAGE = 5; // Snapshot at min batt voltage over 24hrs.
+ MAX_VOLTAGE = 6; // Snapshot at max batt voltage over 24hrs.
+ MIN_CURRENT = 7; // Snapshot at min batt current over 24hrs.
+ MAX_CURRENT = 8; // Snapshot at max batt current over 24hrs.
+ MIN_BATT_LEVEL = 9; // Snapshot at min battery level (SoC) over 24hrs.
+ MAX_BATT_LEVEL = 10; // Snapshot at max battery level (SoC) over 24hrs.
+ AVG_RESISTANCE = 11; // Snapshot at average battery resistance over 24hrs.
+ }
+ optional BatterySnapshotType type = 1;
+ // Temperature, in 1/10ths of degree C.
+ optional int32 temperature_deci_celcius = 2;
+ // Voltage Battery Voltage, in microVolts.
+ optional int32 voltage_micro_volt = 3;
+ // Current Battery current, in microAmps.
+ optional int32 current_micro_amps = 4;
+ // OpenCircuitVoltage Battery Open Circuit Voltage, in microVolts.
+ optional int32 open_circuit_micro_volt = 5;
+ // Resistance Battery Resistance, in microOhms.
+ optional int32 resistance_micro_ohm = 6;
+ // Level Battery Level, as % of full.
+ optional int32 level_percent = 7;
+}
+
+/**
+ * Log slow I/O operations on the primary storage.
+ */
+message SlowIo {
+ // Classifications of IO Operations.
+ enum IoOperation {
+ UNKNOWN = 0;
+ READ = 1;
+ WRITE = 2;
+ UNMAP = 3;
+ SYNC = 4;
+ }
+ optional IoOperation operation = 1;
+
+ // The number of slow IO operations of this type over 24 hours.
+ optional int32 count = 2;
+}
+
+/**
+ * Log battery caused shutdown with the last recorded voltage.
+ */
+message BatteryCausedShutdown {
+ // The last recorded battery voltage prior to shutdown.
+ optional int32 last_recorded_micro_volt = 1;
+}
+
+/**
* Logs the duration of a davey (jank of >=700ms) when it occurs
*
* Logged from:
@@ -1890,6 +1977,69 @@
// The type of error.
optional Error error = 3;
}
+
+message Notification {
+
+ // Type of notification event.
+ enum Type {
+ TYPE_UNKNOWN = 0;
+ // Notification became visible to the user.
+ TYPE_OPEN = 1;
+ // Notification became hidden.
+ TYPE_CLOSE = 2;
+ // Notification switched to detail mode.
+ TYPE_DETAIL = 3;
+ // Notification was clicked.
+ TYPE_ACTION = 4;
+ // Notification was dismissed.
+ TYPE_DISMISS = 5;
+ // Notification switched to summary mode. The enum value of 14 is to
+ // match that of metrics_constants.
+ TYPE_COLLAPSE = 14;
+ }
+ optional Type type = 1;
+
+ // Package name associated with the notification.
+ optional string package_name = 2;
+
+ // Tag associated with notification.
+ optional string tag = 3;
+
+ // Application-supplied ID associated with the notification.
+ optional int32 id = 4;
+
+ // Index of notification in the notification panel.
+ optional int32 shade_index = 5;
+
+ // The number of notifications in the notification panel.
+ optional int32 shade_count = 6;
+
+ // Importance for the notification.
+ optional int32 importance = 7;
+
+ // ID for the notification channel.
+ optional int32 channel_id = 8;
+
+ // Importance for the notification channel.
+ optional int32 channel_importance = 9;
+
+ // Whether notification was a group summary.
+ optional bool group_summary = 10;
+
+ // Time since notification was created in milliseconds.
+ optional int64 since_create_millis = 11;
+
+ // Time since notification was interrupted in milliseconds.
+ optional int64 since_interruption_millis = 12;
+
+ // Time since notification was updated in milliseconds.
+ optional int64 since_update_millis = 13;
+
+ // Time since notification was visible in milliseconds.
+ optional int64 since_visible_millis = 14;
+}
+
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -2155,6 +2305,11 @@
// 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.
+ optional int64 rss_high_watermark_in_bytes = 9;
}
/*
@@ -2339,7 +2494,7 @@
* Next tag: 11
*/
message LooperStats {
- // Currently not collected and always set to 0.
+ // The uid that made a call to the System Server and caused the message to be enqueued.
optional int32 uid = 1 [(is_uid) = true];
// Fully qualified class name of the handler target class.
@@ -2468,6 +2623,30 @@
}
/**
+ * Pulls per uid I/O stats. The stats are cumulative since boot.
+ *
+ * Read/write bytes are I/O events from a storage device
+ * Read/write chars are data requested by read/write syscalls, and can be
+ * satisfied by caching.
+ *
+ * Pulled from StatsCompanionService, which reads proc/uid_io/stats.
+ */
+message DiskIo {
+ optional int32 uid = 1 [(is_uid) = true];
+ optional int64 fg_chars_read = 2;
+ optional int64 fg_chars_write = 3;
+ optional int64 fg_bytes_read = 4;
+ optional int64 fg_bytes_write = 5;
+ optional int64 bg_chars_read = 6;
+ optional int64 bg_chars_write = 7;
+ optional int64 bg_bytes_read = 8;
+ optional int64 bg_bytes_write = 9;
+ optional int64 fg_fsync = 10;
+ optional int64 bg_fsync= 11;
+}
+
+
+/**
* Pulls the number of fingerprints for each user.
*
* Pulled from StatsCompanionService, which queries FingerprintManager.
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 5a0172b..fd86714 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -163,13 +163,10 @@
new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// battery_voltage
{android::util::BATTERY_VOLTAGE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+ {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {{4, 5, 6, 7, 8},
+ {{4, 5, 6, 7, 8, 9},
{2, 3},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
@@ -204,17 +201,23 @@
{{}, {}, 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)}},
- };
+ // ProcStats.
+ {android::util::PROC_STATS,
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+ // Disk I/O stats per uid.
+ {android::util::DISK_IO,
+ {{2,3,4,5,6,7,8,9,10,11},
+ {},
+ 3 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DISK_IO)}},
+};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 33f3917..bf0bfec 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -400,36 +400,35 @@
return string(timeBuffer);
}
-void StatsdStats::dumpStats(FILE* out) const {
+void StatsdStats::dumpStats(int out) const {
lock_guard<std::mutex> lock(mLock);
time_t t = mStartTimeSec;
struct tm* tm = localtime(&t);
char timeBuffer[80];
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
- fprintf(out, "Stats collection start second: %s\n", timeBuffer);
- fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
+ dprintf(out, "Stats collection start second: %s\n", timeBuffer);
+ dprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
for (const auto& configStats : mIceBox) {
- fprintf(out,
+ dprintf(out,
"Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
configStats->deletion_time_sec, configStats->reset_time_sec,
- configStats->metric_count,
- configStats->condition_count, configStats->matcher_count, configStats->alert_count,
- configStats->is_valid);
+ configStats->metric_count, configStats->condition_count, configStats->matcher_count,
+ configStats->alert_count, configStats->is_valid);
for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
+ dprintf(out, "\tbroadcast time: %d\n", broadcastTime);
}
for (const auto& dataDropTime : configStats->data_drop_time_sec) {
- fprintf(out, "\tdata drop time: %d\n", dataDropTime);
+ dprintf(out, "\tdata drop time: %d\n", dataDropTime);
}
}
- fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
+ dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
for (auto& pair : mConfigStats) {
auto& configStats = pair.second;
- fprintf(out,
+ dprintf(out,
"Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
@@ -437,81 +436,81 @@
configStats->condition_count, configStats->matcher_count, configStats->alert_count,
configStats->is_valid);
for (const auto& annotation : configStats->annotations) {
- fprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first,
+ dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first,
annotation.second);
}
for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- fprintf(out, "\tbroadcast time: %s(%lld)\n",
- buildTimeString(broadcastTime).c_str(), (long long)broadcastTime);
+ dprintf(out, "\tbroadcast time: %s(%lld)\n", buildTimeString(broadcastTime).c_str(),
+ (long long)broadcastTime);
}
for (const auto& dataDropTime : configStats->data_drop_time_sec) {
- fprintf(out, "\tdata drop time: %s(%lld)\n",
- buildTimeString(dataDropTime).c_str(), (long long)dataDropTime);
+ dprintf(out, "\tdata drop time: %s(%lld)\n", buildTimeString(dataDropTime).c_str(),
+ (long long)dataDropTime);
}
for (const auto& dump : configStats->dump_report_stats) {
- fprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
+ dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
buildTimeString(dump.first).c_str(), (long long)dump.first,
(long long)dump.second);
}
for (const auto& stats : pair.second->matcher_stats) {
- fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second);
+ dprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second);
}
for (const auto& stats : pair.second->condition_stats) {
- fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
+ dprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
stats.second);
}
for (const auto& stats : pair.second->condition_stats) {
- fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
+ dprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
stats.second);
}
for (const auto& stats : pair.second->alert_stats) {
- fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
+ dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
}
}
- fprintf(out, "********Disk Usage stats***********\n");
+ dprintf(out, "********Disk Usage stats***********\n");
StorageManager::printStats(out);
- fprintf(out, "********Pushed Atom stats***********\n");
+ dprintf(out, "********Pushed Atom stats***********\n");
const size_t atomCounts = mPushedAtomStats.size();
for (size_t i = 2; i < atomCounts; i++) {
if (mPushedAtomStats[i] > 0) {
- fprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+ dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
}
}
- fprintf(out, "********Pulled Atom stats***********\n");
+ dprintf(out, "********Pulled Atom stats***********\n");
for (const auto& pair : mPulledAtomStats) {
- fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
+ dprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
(long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
}
if (mAnomalyAlarmRegisteredStats > 0) {
- fprintf(out, "********AnomalyAlarmStats stats***********\n");
- fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
+ dprintf(out, "********AnomalyAlarmStats stats***********\n");
+ dprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
}
if (mPeriodicAlarmRegisteredStats > 0) {
- fprintf(out, "********SubscriberAlarmStats stats***********\n");
- fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
+ dprintf(out, "********SubscriberAlarmStats stats***********\n");
+ dprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
}
- fprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n",
+ dprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n",
mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps,
mUidMapStats.dropped_changes);
for (const auto& restart : mSystemServerRestartSec) {
- fprintf(out, "System server restarts at %s(%lld)\n",
- buildTimeString(restart).c_str(), (long long)restart);
+ dprintf(out, "System server restarts at %s(%lld)\n", buildTimeString(restart).c_str(),
+ (long long)restart);
}
for (const auto& loss : mLogLossStats) {
- fprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first,
+ dprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first,
loss.second);
}
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index b5156da..a8188c8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -156,7 +156,7 @@
* Report a config has been removed.
*/
void noteConfigRemoved(const ConfigKey& key);
- /**
+ /**
* Report a config has been reset when ttl expires.
*/
void noteConfigReset(const ConfigKey& key);
@@ -202,7 +202,6 @@
*/
void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
-
/**
* Report the max size of output tuple of dimension in condition across dimensions in what.
*
@@ -272,8 +271,8 @@
void notePullFromCache(int pullAtomId);
/*
- * Records when system server restarts.
- */
+ * Records when system server restarts.
+ */
void noteSystemServerRestart(int32_t timeSec);
/**
@@ -296,9 +295,9 @@
void dumpStats(std::vector<uint8_t>* buffer, bool reset);
/**
- * Output statsd stats in human readable format to [out] file.
+ * Output statsd stats in human readable format to [out] file descriptor.
*/
- void dumpStats(FILE* out) const;
+ void dumpStats(int outFd) const;
typedef struct {
long totalPull;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index f9f1b38..4bbcfd5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -92,7 +92,8 @@
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
int32_t uid,
- const std::map<int32_t, int64_t>& int_map,
+ const std::map<int32_t, int32_t>& int_map,
+ const std::map<int32_t, int64_t>& long_map,
const std::map<int32_t, std::string>& string_map,
const std::map<int32_t, float>& float_map) {
mLogdTimestampNs = wallClockTimestampNs;
@@ -113,7 +114,7 @@
pos[1]++;
}
- for (const auto&itr : string_map) {
+ for (const auto&itr : long_map) {
pos[2] = 1;
mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
pos[2] = 3;
@@ -122,7 +123,7 @@
pos[1]++;
}
- for (const auto&itr : float_map) {
+ for (const auto&itr : string_map) {
pos[2] = 1;
mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
pos[2] = 4;
@@ -130,12 +131,142 @@
mValues.back().mField.decorateLastPos(2);
pos[1]++;
}
+
+ for (const auto&itr : float_map) {
+ pos[2] = 1;
+ mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
+ pos[2] = 5;
+ mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
+ mValues.back().mField.decorateLastPos(2);
+ pos[1]++;
+ }
if (!mValues.empty()) {
mValues.back().mField.decorateLastPos(1);
mValues.at(mValues.size() - 2).mField.decorateLastPos(1);
}
}
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SpeakerImpedance& speakerImpedance) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::SPEAKER_IMPEDANCE_REPORTED;
+
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(1)), Value(speakerImpedance.speakerLocation)));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(2)), Value(speakerImpedance.milliOhms)));
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const HardwareFailed& hardwareFailed) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::HARDWARE_FAILED;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(int32_t(hardwareFailed.hardwareType))));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(2)), Value(hardwareFailed.hardwareLocation)));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(3)), Value(int32_t(hardwareFailed.errorCode))));
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const PhysicalDropDetected& physicalDropDetected) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::PHYSICAL_DROP_DETECTED;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(int32_t(physicalDropDetected.confidencePctg))));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(2)), Value(physicalDropDetected.accelPeak)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+ Value(physicalDropDetected.freefallDuration)));
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const ChargeCycles& chargeCycles) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::CHARGE_CYCLES_REPORTED;
+
+ for (size_t i = 0; i < chargeCycles.cycleBucket.size(); i++) {
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 1)),
+ Value(chargeCycles.cycleBucket[i])));
+ }
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::BATTERY_HEALTH_SNAPSHOT;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(int32_t(batteryHealthSnapshotArgs.type))));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)),
+ Value(batteryHealthSnapshotArgs.temperatureDeciC)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+ Value(batteryHealthSnapshotArgs.voltageMicroV)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)),
+ Value(batteryHealthSnapshotArgs.currentMicroA)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(5)),
+ Value(batteryHealthSnapshotArgs.openCircuitVoltageMicroV)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)),
+ Value(batteryHealthSnapshotArgs.resistanceMicroOhm)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)),
+ Value(batteryHealthSnapshotArgs.levelPercent)));
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SlowIo& slowIo) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::SLOW_IO;
+
+ int pos[] = {1};
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(1)), Value(int32_t(slowIo.operation))));
+ pos[0]++;
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(slowIo.count)));
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryCausedShutdown& batteryCausedShutdown) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::BATTERY_CAUSED_SHUTDOWN;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(batteryCausedShutdown.voltageMicroV)));
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) {
mLogdTimestampNs = timestampNs;
mTagId = tagId;
@@ -213,9 +344,8 @@
return false;
}
-
-
-bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map,
+bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int32_t>& int_map,
+ const std::map<int32_t, int64_t>& long_map,
const std::map<int32_t, std::string>& string_map,
const std::map<int32_t, float>& float_map) {
if (mContext) {
@@ -233,6 +363,17 @@
}
}
+ for (const auto& itr : long_map) {
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ write(itr.first);
+ write(itr.second);
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ }
+
for (const auto& itr : string_map) {
if (android_log_write_list_begin(mContext) < 0) {
return false;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 9ef0bf4..c7e2a8c 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
#include "FieldValue.h"
+#include <android/frameworks/stats/1.0/types.h>
#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
@@ -28,6 +29,8 @@
#include <string>
#include <vector>
+using namespace android::frameworks::stats::V1_0;
+
namespace android {
namespace os {
namespace statsd {
@@ -77,10 +80,32 @@
*/
explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
int32_t uid,
- const std::map<int32_t, int64_t>& int_map,
+ const std::map<int32_t, int32_t>& int_map,
+ const std::map<int32_t, int64_t>& long_map,
const std::map<int32_t, std::string>& string_map,
const std::map<int32_t, float>& float_map);
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SpeakerImpedance& speakerImpedance);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const HardwareFailed& hardwareFailed);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const PhysicalDropDetected& physicalDropDetected);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const ChargeCycles& chargeCycles);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SlowIo& slowIo);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryCausedShutdown& batteryCausedShutdown);
+
~LogEvent();
/**
@@ -122,7 +147,8 @@
bool write(float value);
bool write(const std::vector<AttributionNodeInternal>& nodes);
bool write(const AttributionNodeInternal& node);
- bool writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map,
+ bool writeKeyValuePairs(const std::map<int32_t, int32_t>& int_map,
+ const std::map<int32_t, int64_t>& long_map,
const std::map<int32_t, std::string>& string_map,
const std::map<int32_t, float>& float_map);
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 9002f07..a5dac08 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -25,6 +25,7 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/Status.h>
+#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
@@ -56,12 +57,21 @@
ps->giveThreadPoolName();
IPCThreadState::self()->disableBackgroundScheduling(true);
+ ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/);
+
// Create the service
sp<StatsService> service = new StatsService(looper);
if (defaultServiceManager()->addService(String16("stats"), service) != 0) {
- ALOGE("Failed to add service");
+ ALOGE("Failed to add service as AIDL service");
return -1;
}
+
+ auto ret = service->registerAsService();
+ if (ret != ::android::OK) {
+ ALOGE("Failed to add service as HIDL service");
+ return 1; // or handle error
+ }
+
service->sayHiToStatsCompanion();
service->Startup();
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 9d9e5be9..dd3402d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -781,7 +781,7 @@
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
const bool cond = dimensionKeysInCondition.find(condIt.first) !=
- dimensionKeysInCondition.end();
+ dimensionKeysInCondition.end() && condition;
handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
conditionKey, cond, event);
dimensionKeysInCondition.erase(condIt.first);
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index be94725..4325f0f 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -386,12 +386,12 @@
StatsdStats::getInstance().setUidMapChanges(mChanges.size());
}
-void UidMap::printUidMap(FILE* out) const {
+void UidMap::printUidMap(int out) const {
lock_guard<mutex> lock(mMutex);
for (const auto& kv : mMap) {
if (!kv.second.deleted) {
- fprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
+ dprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
kv.first.first);
}
}
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 91f2030..4598369 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -103,7 +103,7 @@
// Helper for debugging contents of this uid map. Can be triggered with:
// adb shell cmd stats print-uid-map
- void printUidMap(FILE* out) const;
+ void printUidMap(int outFd) const;
// Commands for indicating to the map that a producer should be notified if an app is updated.
// This allows the metric producer to distinguish when the same uid or app represents a
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
new file mode 100644
index 0000000..1306a46
--- /dev/null
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -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.
+ */
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "ShellSubscriber.h"
+
+#include "matchers/matcher_util.h"
+
+#include <android-base/file.h>
+
+using android::util::ProtoOutputStream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) {
+ VLOG("start new shell subscription");
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mResultReceiver != nullptr) {
+ VLOG("Only one shell subscriber is allowed.");
+ return;
+ }
+ mInput = in;
+ mOutput = out;
+ mResultReceiver = resultReceiver;
+ IInterface::asBinder(mResultReceiver)->linkToDeath(this);
+ }
+
+ // Spawn another thread to read the config updates from the input file descriptor
+ std::thread reader([in, this] { readConfig(in); });
+ reader.detach();
+
+ std::unique_lock<std::mutex> lk(mMutex);
+
+ mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+ if (reader.joinable()) {
+ reader.join();
+ }
+}
+
+void ShellSubscriber::updateConfig(const ShellSubscription& config) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPushedMatchers.clear();
+ for (const auto& pushed : config.pushed()) {
+ mPushedMatchers.push_back(pushed);
+ VLOG("adding matcher for atom %d", pushed.atom_id());
+ }
+}
+
+void ShellSubscriber::readConfig(int in) {
+ if (in <= 0) {
+ return;
+ }
+
+ while (1) {
+ size_t bufferSize = 0;
+ int result = 0;
+ if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
+ VLOG("Done reading");
+ break;
+ } else if (result < 0 || result != sizeof(bufferSize)) {
+ ALOGE("Error reading config size");
+ break;
+ }
+
+ vector<uint8_t> buffer(bufferSize);
+ if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
+ ShellSubscription config;
+ if (config.ParseFromArray(buffer.data(), bufferSize)) {
+ updateConfig(config);
+ } else {
+ ALOGE("error parsing the config");
+ break;
+ }
+ } else {
+ VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
+ break;
+ }
+ }
+}
+
+void ShellSubscriber::cleanUpLocked() {
+ // The file descriptors will be closed by binder.
+ mInput = 0;
+ mOutput = 0;
+ mResultReceiver = nullptr;
+ mPushedMatchers.clear();
+ VLOG("done clean up");
+}
+
+void ShellSubscriber::onLogEvent(const LogEvent& event) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mOutput <= 0) {
+ return;
+ }
+
+ for (const auto& matcher : mPushedMatchers) {
+ if (matchesSimple(*mUidMap, matcher, event)) {
+ event.ToProto(mProto);
+ // First write the payload size.
+ size_t bufferSize = mProto.size();
+ write(mOutput, &bufferSize, sizeof(bufferSize));
+
+ // Then write the payload.
+ mProto.flush(mOutput);
+ mProto.clear();
+ break;
+ }
+ }
+}
+
+void ShellSubscriber::binderDied(const wp<IBinder>& who) {
+ {
+ VLOG("Shell exits");
+ std::lock_guard<std::mutex> lock(mMutex);
+ cleanUpLocked();
+ }
+ mShellDied.notify_all();
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
new file mode 100644
index 0000000..0ace35f
--- /dev/null
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "logd/LogEvent.h"
+
+#include <android/util/ProtoOutputStream.h>
+#include <binder/IResultReceiver.h>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Handles atoms subscription via shell cmd.
+ *
+ * A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client
+ * communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms.
+ * The atoms are sent back to the client in real time, as opposed to
+ * keeping the data in memory. Shell clients do not subscribe aggregated metrics, as they are
+ * responsible for doing the aggregation after receiving the atom events.
+ *
+ * Shell client pass ShellSubscription in the proto binary format. Client can update the
+ * subscription by sending a new subscription. The new subscription would replace the old one.
+ * Input data stream format is:
+ *
+ * |size_t|subscription proto|size_t|subscription proto|....
+ *
+ * statsd sends the events back in Atom proto binary format. Each Atom message is preceded
+ * with sizeof(size_t) bytes indicating the size of the proto message payload.
+ *
+ * The stream would be in the following format:
+ * |size_t|atom1 proto|size_t|atom2 proto|....
+ *
+ * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
+ * until it exits.
+ */
+class ShellSubscriber : public virtual IBinder::DeathRecipient {
+public:
+ ShellSubscriber(sp<UidMap> uidMap) : mUidMap(uidMap){};
+
+ /**
+ * Start a new subscription.
+ */
+ void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver);
+
+ void binderDied(const wp<IBinder>& who);
+
+ void onLogEvent(const LogEvent& event);
+
+private:
+ void readConfig(int in);
+
+ void updateConfig(const ShellSubscription& config);
+
+ void cleanUpLocked();
+
+ sp<UidMap> mUidMap;
+
+ // bool mWritten = false;
+
+ android::util::ProtoOutputStream mProto;
+
+ mutable std::mutex mMutex;
+
+ std::condition_variable mShellDied; // semaphore for waiting until shell exits.
+
+ int mInput; // The input file descriptor
+
+ int mOutput; // The output file descriptor
+
+ sp<IResultReceiver> mResultReceiver;
+
+ std::vector<SimpleAtomMatcher> mPushedMatchers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
new file mode 100644
index 0000000..516693d
--- /dev/null
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+
+option java_package = "com.android.os";
+option java_outer_classname = "ShellConfig";
+
+import "frameworks/base/cmds/statsd/src/statsd_config.proto";
+
+message PulledAtomSubscription {
+ optional int32 atom_id = 1;
+
+ /* gap between two pulls in milliseconds */
+ optional int32 freq_millis = 2;
+}
+
+message ShellSubscription {
+ repeated SimpleAtomMatcher pushed = 1;
+ repeated PulledAtomSubscription pulled = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 3ebc8a4..2f19a02 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -392,13 +392,13 @@
}
}
-void StorageManager::printStats(FILE* out) {
- printDirStats(out, STATS_SERVICE_DIR);
- printDirStats(out, STATS_DATA_DIR);
+void StorageManager::printStats(int outFd) {
+ printDirStats(outFd, STATS_SERVICE_DIR);
+ printDirStats(outFd, STATS_DATA_DIR);
}
-void StorageManager::printDirStats(FILE* out, const char* path) {
- fprintf(out, "Printing stats of %s\n", path);
+void StorageManager::printDirStats(int outFd, const char* path) {
+ dprintf(outFd, "Printing stats of %s\n", path);
unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", path);
@@ -418,25 +418,22 @@
int64_t timestamp = result[0];
int64_t uid = result[1];
int64_t configID = result[2];
- fprintf(out, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld",
- fileCount + 1,
- (long long)timestamp,
- (int)uid,
- (long long)configID);
+ dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", fileCount + 1,
+ (long long)timestamp, (int)uid, (long long)configID);
string file_name = getFilePath(path, timestamp, uid, configID);
ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
if (file.is_open()) {
file.seekg(0, ios::end);
int fileSize = file.tellg();
file.close();
- fprintf(out, ", File Size: %d bytes", fileSize);
+ dprintf(outFd, ", File Size: %d bytes", fileSize);
totalFileSize += fileSize;
}
- fprintf(out, "\n");
+ dprintf(outFd, "\n");
fileCount++;
}
- fprintf(out, "\tTotal number of files: %d, Total size of files: %d bytes.\n",
- fileCount, totalFileSize);
+ dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
+ totalFileSize);
}
} // namespace statsd
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 4840f3c..8fbc89e 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -100,13 +100,13 @@
/**
* Prints disk usage statistics related to statsd.
*/
- static void printStats(FILE* out);
+ static void printStats(int out);
private:
/**
* Prints disk usage statistics about a directory related to statsd.
*/
- static void printDirStats(FILE* out, const char* path);
+ static void printDirStats(int out, const char* path);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6e3b04c..d490701 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -91,12 +91,16 @@
TEST(LogEventTest, TestKeyValuePairsAtomParsing) {
LogEvent event1(83, 2000);
- std::map<int32_t, int64_t> int_map;
+ std::map<int32_t, int32_t> int_map;
+ std::map<int32_t, int64_t> long_map;
std::map<int32_t, std::string> string_map;
std::map<int32_t, float> float_map;
- int_map[11] = 123L;
- int_map[22] = 345L;
+ int_map[11] = 123;
+ int_map[22] = 345;
+
+ long_map[33] = 678L;
+ long_map[44] = 890L;
string_map[1] = "test2";
string_map[2] = "test1";
@@ -104,12 +108,15 @@
float_map[111] = 2.2f;
float_map[222] = 1.1f;
- EXPECT_TRUE(event1.writeKeyValuePairs(int_map, string_map, float_map));
+ EXPECT_TRUE(event1.writeKeyValuePairs(int_map,
+ long_map,
+ string_map,
+ float_map));
event1.init();
EXPECT_EQ(83, event1.GetTagId());
const auto& items = event1.getValues();
- EXPECT_EQ((size_t)12, items.size());
+ EXPECT_EQ((size_t)16, items.size());
const FieldValue& item0 = event1.getValues()[0];
EXPECT_EQ(0x2010101, item0.mField.getField());
@@ -118,8 +125,8 @@
const FieldValue& item1 = event1.getValues()[1];
EXPECT_EQ(0x2010182, item1.mField.getField());
- EXPECT_EQ(Type::LONG, item1.mValue.getType());
- EXPECT_EQ(123L, item1.mValue.long_value);
+ EXPECT_EQ(Type::INT, item1.mValue.getType());
+ EXPECT_EQ(123, item1.mValue.int_value);
const FieldValue& item2 = event1.getValues()[2];
EXPECT_EQ(0x2010201, item2.mField.getField());
@@ -128,48 +135,68 @@
const FieldValue& item3 = event1.getValues()[3];
EXPECT_EQ(0x2010282, item3.mField.getField());
- EXPECT_EQ(Type::LONG, item3.mValue.getType());
- EXPECT_EQ(345L, item3.mValue.long_value);
+ EXPECT_EQ(Type::INT, item3.mValue.getType());
+ EXPECT_EQ(345, item3.mValue.int_value);
const FieldValue& item4 = event1.getValues()[4];
EXPECT_EQ(0x2010301, item4.mField.getField());
EXPECT_EQ(Type::INT, item4.mValue.getType());
- EXPECT_EQ(1, item4.mValue.int_value);
+ EXPECT_EQ(33, item4.mValue.int_value);
const FieldValue& item5 = event1.getValues()[5];
- EXPECT_EQ(0x2010383, item5.mField.getField());
- EXPECT_EQ(Type::STRING, item5.mValue.getType());
- EXPECT_EQ("test2", item5.mValue.str_value);
+ EXPECT_EQ(0x2010382, item5.mField.getField());
+ EXPECT_EQ(Type::LONG, item5.mValue.getType());
+ EXPECT_EQ(678L, item5.mValue.int_value);
const FieldValue& item6 = event1.getValues()[6];
EXPECT_EQ(0x2010401, item6.mField.getField());
EXPECT_EQ(Type::INT, item6.mValue.getType());
- EXPECT_EQ(2, item6.mValue.int_value);
+ EXPECT_EQ(44, item6.mValue.int_value);
const FieldValue& item7 = event1.getValues()[7];
- EXPECT_EQ(0x2010483, item7.mField.getField());
- EXPECT_EQ(Type::STRING, item7.mValue.getType());
- EXPECT_EQ("test1", item7.mValue.str_value);
+ EXPECT_EQ(0x2010482, item7.mField.getField());
+ EXPECT_EQ(Type::LONG, item7.mValue.getType());
+ EXPECT_EQ(890L, item7.mValue.int_value);
const FieldValue& item8 = event1.getValues()[8];
EXPECT_EQ(0x2010501, item8.mField.getField());
EXPECT_EQ(Type::INT, item8.mValue.getType());
- EXPECT_EQ(111, item8.mValue.int_value);
+ EXPECT_EQ(1, item8.mValue.int_value);
const FieldValue& item9 = event1.getValues()[9];
- EXPECT_EQ(0x2010584, item9.mField.getField());
- EXPECT_EQ(Type::FLOAT, item9.mValue.getType());
- EXPECT_EQ(2.2f, item9.mValue.float_value);
+ EXPECT_EQ(0x2010583, item9.mField.getField());
+ EXPECT_EQ(Type::STRING, item9.mValue.getType());
+ EXPECT_EQ("test2", item9.mValue.str_value);
const FieldValue& item10 = event1.getValues()[10];
- EXPECT_EQ(0x2018601, item10.mField.getField());
+ EXPECT_EQ(0x2010601, item10.mField.getField());
EXPECT_EQ(Type::INT, item10.mValue.getType());
- EXPECT_EQ(222, item10.mValue.int_value);
+ EXPECT_EQ(2, item10.mValue.int_value);
const FieldValue& item11 = event1.getValues()[11];
- EXPECT_EQ(0x2018684, item11.mField.getField());
- EXPECT_EQ(Type::FLOAT, item11.mValue.getType());
- EXPECT_EQ(1.1f, item11.mValue.float_value);
+ EXPECT_EQ(0x2010683, item11.mField.getField());
+ EXPECT_EQ(Type::STRING, item11.mValue.getType());
+ EXPECT_EQ("test1", item11.mValue.str_value);
+
+ const FieldValue& item12 = event1.getValues()[12];
+ EXPECT_EQ(0x2010701, item12.mField.getField());
+ EXPECT_EQ(Type::INT, item12.mValue.getType());
+ EXPECT_EQ(111, item12.mValue.int_value);
+
+ const FieldValue& item13 = event1.getValues()[13];
+ EXPECT_EQ(0x2010784, item13.mField.getField());
+ EXPECT_EQ(Type::FLOAT, item13.mValue.getType());
+ EXPECT_EQ(2.2f, item13.mValue.float_value);
+
+ const FieldValue& item14 = event1.getValues()[14];
+ EXPECT_EQ(0x2018801, item14.mField.getField());
+ EXPECT_EQ(Type::INT, item14.mValue.getType());
+ EXPECT_EQ(222, item14.mValue.int_value);
+
+ const FieldValue& item15 = event1.getValues()[15];
+ EXPECT_EQ(0x2018884, item15.mField.getField());
+ EXPECT_EQ(Type::FLOAT, item15.mValue.getType());
+ EXPECT_EQ(1.1f, item15.mValue.float_value);
}
TEST(LogEventTest, TestLogParsing2) {
@@ -242,12 +269,16 @@
}
TEST(LogEventTest, TestKeyValuePairsEvent) {
- std::map<int32_t, int64_t> int_map;
+ std::map<int32_t, int32_t> int_map;
+ std::map<int32_t, int64_t> long_map;
std::map<int32_t, std::string> string_map;
std::map<int32_t, float> float_map;
- int_map[11] = 123L;
- int_map[22] = 345L;
+ int_map[11] = 123;
+ int_map[22] = 345;
+
+ long_map[33] = 678L;
+ long_map[44] = 890L;
string_map[1] = "test2";
string_map[2] = "test1";
@@ -255,7 +286,7 @@
float_map[111] = 2.2f;
float_map[222] = 1.1f;
- LogEvent event1(83, 2000, 2001, 10001, int_map, string_map, float_map);
+ LogEvent event1(83, 2000, 2001, 10001, int_map, long_map, string_map, float_map);
event1.init();
EXPECT_EQ(83, event1.GetTagId());
@@ -263,7 +294,7 @@
EXPECT_EQ((int64_t)2001, event1.GetElapsedTimestampNs());
const auto& items = event1.getValues();
- EXPECT_EQ((size_t)13, items.size());
+ EXPECT_EQ((size_t)17, items.size());
const FieldValue& item0 = event1.getValues()[0];
EXPECT_EQ(0x00010000, item0.mField.getField());
@@ -277,8 +308,8 @@
const FieldValue& item2 = event1.getValues()[2];
EXPECT_EQ(0x2020182, item2.mField.getField());
- EXPECT_EQ(Type::LONG, item2.mValue.getType());
- EXPECT_EQ(123L, item2.mValue.long_value);
+ EXPECT_EQ(Type::INT, item2.mValue.getType());
+ EXPECT_EQ(123, item2.mValue.int_value);
const FieldValue& item3 = event1.getValues()[3];
EXPECT_EQ(0x2020201, item3.mField.getField());
@@ -287,48 +318,68 @@
const FieldValue& item4 = event1.getValues()[4];
EXPECT_EQ(0x2020282, item4.mField.getField());
- EXPECT_EQ(Type::LONG, item4.mValue.getType());
- EXPECT_EQ(345L, item4.mValue.long_value);
+ EXPECT_EQ(Type::INT, item4.mValue.getType());
+ EXPECT_EQ(345, item4.mValue.int_value);
const FieldValue& item5 = event1.getValues()[5];
EXPECT_EQ(0x2020301, item5.mField.getField());
EXPECT_EQ(Type::INT, item5.mValue.getType());
- EXPECT_EQ(1, item5.mValue.int_value);
+ EXPECT_EQ(33, item5.mValue.int_value);
const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x2020383, item6.mField.getField());
- EXPECT_EQ(Type::STRING, item6.mValue.getType());
- EXPECT_EQ("test2", item6.mValue.str_value);
+ EXPECT_EQ(0x2020382, item6.mField.getField());
+ EXPECT_EQ(Type::LONG, item6.mValue.getType());
+ EXPECT_EQ(678L, item6.mValue.long_value);
const FieldValue& item7 = event1.getValues()[7];
EXPECT_EQ(0x2020401, item7.mField.getField());
EXPECT_EQ(Type::INT, item7.mValue.getType());
- EXPECT_EQ(2, item7.mValue.int_value);
+ EXPECT_EQ(44, item7.mValue.int_value);
const FieldValue& item8 = event1.getValues()[8];
- EXPECT_EQ(0x2020483, item8.mField.getField());
- EXPECT_EQ(Type::STRING, item8.mValue.getType());
- EXPECT_EQ("test1", item8.mValue.str_value);
+ EXPECT_EQ(0x2020482, item8.mField.getField());
+ EXPECT_EQ(Type::LONG, item8.mValue.getType());
+ EXPECT_EQ(890L, item8.mValue.long_value);
const FieldValue& item9 = event1.getValues()[9];
EXPECT_EQ(0x2020501, item9.mField.getField());
EXPECT_EQ(Type::INT, item9.mValue.getType());
- EXPECT_EQ(111, item9.mValue.int_value);
+ EXPECT_EQ(1, item9.mValue.int_value);
const FieldValue& item10 = event1.getValues()[10];
- EXPECT_EQ(0x2020584, item10.mField.getField());
- EXPECT_EQ(Type::FLOAT, item10.mValue.getType());
- EXPECT_EQ(2.2f, item10.mValue.float_value);
+ EXPECT_EQ(0x2020583, item10.mField.getField());
+ EXPECT_EQ(Type::STRING, item10.mValue.getType());
+ EXPECT_EQ("test2", item10.mValue.str_value);
const FieldValue& item11 = event1.getValues()[11];
- EXPECT_EQ(0x2028601, item11.mField.getField());
+ EXPECT_EQ(0x2020601, item11.mField.getField());
EXPECT_EQ(Type::INT, item11.mValue.getType());
- EXPECT_EQ(222, item11.mValue.int_value);
+ EXPECT_EQ(2, item11.mValue.int_value);
const FieldValue& item12 = event1.getValues()[12];
- EXPECT_EQ(0x2028684, item12.mField.getField());
- EXPECT_EQ(Type::FLOAT, item12.mValue.getType());
- EXPECT_EQ(1.1f, item12.mValue.float_value);
+ EXPECT_EQ(0x2020683, item12.mField.getField());
+ EXPECT_EQ(Type::STRING, item12.mValue.getType());
+ EXPECT_EQ("test1", item12.mValue.str_value);
+
+ const FieldValue& item13 = event1.getValues()[13];
+ EXPECT_EQ(0x2020701, item13.mField.getField());
+ EXPECT_EQ(Type::INT, item13.mValue.getType());
+ EXPECT_EQ(111, item13.mValue.int_value);
+
+ const FieldValue& item14 = event1.getValues()[14];
+ EXPECT_EQ(0x2020784, item14.mField.getField());
+ EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
+ EXPECT_EQ(2.2f, item14.mValue.float_value);
+
+ const FieldValue& item15 = event1.getValues()[15];
+ EXPECT_EQ(0x2028801, item15.mField.getField());
+ EXPECT_EQ(Type::INT, item15.mValue.getType());
+ EXPECT_EQ(222, item15.mValue.int_value);
+
+ const FieldValue& item16 = event1.getValues()[16];
+ EXPECT_EQ(0x2028884, item16.mField.getField());
+ EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
+ EXPECT_EQ(1.1f, item16.mValue.float_value);
}
@@ -337,4 +388,4 @@
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index f038214..75bd40f 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -81,6 +81,34 @@
} // namespace
+/*
+ The following test has the following input.
+
+{ 10000000002 10000000002 (8)9999[I], [S], job0[S], 1[I], }
+{ 10000000010 10000000010 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
+{ 10000000011 10000000011 (29)1[I], }
+{ 10000000040 10000000040 (29)2[I], }
+{ 10000000050 10000000050 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
+{ 10000000101 10000000101 (8)9999[I], [S], job0[S], 0[I], }
+{ 10000000102 10000000102 (29)1[I], }
+{ 10000000200 10000000200 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
+{ 10000000201 10000000201 (8)9999[I], [S], job2[S], 1[I], }
+{ 10000000400 10000000400 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 1[I], }
+{ 10000000401 10000000401 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
+{ 10000000450 10000000450 (29)2[I], }
+{ 10000000500 10000000500 (8)9999[I], [S], job2[S], 0[I], }
+{ 10000000600 10000000600 (8)8888[I], [S], job2[S], 1[I], }
+{ 10000000650 10000000650 (29)1[I], }
+{ 309999999999 309999999999 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 0[I], }
+{ 310000000100 310000000100 (29)2[I], }
+{ 310000000300 310000000300 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
+{ 310000000600 310000000600 (8)8888[I], [S], job1[S], 1[I], }
+{ 310000000640 310000000640 (29)1[I], }
+{ 310000000650 310000000650 (29)2[I], }
+{ 310000000700 310000000700 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
+{ 310000000850 310000000850 (8)8888[I], [S], job2[S], 0[I], }
+{ 310000000900 310000000900 (8)8888[I], [S], job1[S], 0[I], }
+*/
TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) {
for (const bool hashStringInReport : { true, false }) {
for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) {
@@ -250,7 +278,7 @@
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 650);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
@@ -269,7 +297,7 @@
data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 650);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
@@ -331,7 +359,7 @@
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
@@ -353,7 +381,7 @@
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 110);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 110);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
new file mode 100644
index 0000000..b380b03
--- /dev/null
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include <unistd.h>
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
+#include "src/shell/ShellSubscriber.h"
+#include "tests/metrics/metrics_test_helper.h"
+
+#include <stdio.h>
+#include <vector>
+
+using namespace android::os::statsd;
+using android::sp;
+using std::vector;
+using testing::NaggyMock;
+
+#ifdef __ANDROID__
+
+class MyResultReceiver : public BnResultReceiver {
+public:
+ Mutex mMutex;
+ Condition mCondition;
+ bool mHaveResult = false;
+ int32_t mResult = 0;
+
+ virtual void send(int32_t resultCode) {
+ AutoMutex _l(mMutex);
+ mResult = resultCode;
+ mHaveResult = true;
+ mCondition.signal();
+ }
+
+ int32_t waitForResult() {
+ AutoMutex _l(mMutex);
+ mCondition.waitRelative(mMutex, 1000000000);
+ return mResult;
+ }
+};
+
+TEST(ShellSubscriberTest, testPushedSubscription) {
+ // set up 2 pipes for read/write config and data
+ int fds_config[2];
+ ASSERT_EQ(0, pipe(fds_config));
+
+ int fds_data[2];
+ ASSERT_EQ(0, pipe(fds_data));
+
+ // create a simple config to get screen events
+ ShellSubscription config;
+ config.add_pushed()->set_atom_id(29);
+
+ size_t bufferSize = config.ByteSize();
+
+ // write the config to pipe, first write size of the config
+ vector<uint8_t> size_buffer(sizeof(bufferSize));
+ std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
+ write(fds_config[1], &bufferSize, sizeof(bufferSize));
+ // then write config itself
+ vector<uint8_t> buffer(bufferSize);
+ config.SerializeToArray(&buffer[0], bufferSize);
+ write(fds_config[1], buffer.data(), bufferSize);
+ close(fds_config[1]);
+
+ // create a shell subscriber.
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap);
+ sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
+
+ LogEvent event1(29, 1000);
+ event1.write(2);
+ event1.init();
+
+ // mimic a binder thread that a shell subscriber runs on. it would block.
+ std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
+ shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver);
+ });
+ reader.detach();
+
+ // let the shell subscriber to receive the config from pipe.
+ std::this_thread::sleep_for(100ms);
+
+ // send a log event that matches the config.
+ std::thread log_reader([&shellClient, &event1] { shellClient->onLogEvent(event1); });
+ log_reader.detach();
+
+ if (log_reader.joinable()) {
+ log_reader.join();
+ }
+
+ // wait for the data to be written.
+ std::this_thread::sleep_for(100ms);
+
+ // this is the expected screen event atom.
+ Atom atom;
+ atom.mutable_screen_state_changed()->set_state(
+ ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+ int atom_size = atom.ByteSize();
+
+ // now read from the pipe. firstly read the atom size.
+ size_t dataSize = 0;
+ EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
+ EXPECT_EQ(atom_size, (int)dataSize);
+
+ // then read that much data which is the atom in proto binary format
+ vector<uint8_t> dataBuffer(dataSize);
+ EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
+
+ // make sure the received bytes can be parsed to an atom
+ Atom receivedAtom;
+ EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
+
+ // serialze the expected atom to bytes. and compare. to make sure they are the same.
+ vector<uint8_t> atomBuffer(atom_size);
+ atom.SerializeToArray(&atomBuffer[0], atom_size);
+ EXPECT_EQ(atomBuffer, dataBuffer);
+ close(fds_data[0]);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 950a258..455e4bb 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -84,7 +84,8 @@
cursor.close();
}
if (provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
+ activityManager.removeContentProviderExternalAsUser(providerName, token,
+ UserHandle.USER_SYSTEM);
}
}
} catch (RemoteException e) {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 0472461..6af34f9 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -867,7 +867,6 @@
Landroid/os/PowerManager;->isLightDeviceIdleMode()Z
Landroid/os/PowerManager;->mHandler:Landroid/os/Handler;
Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager;
-Landroid/os/PowerManager;->setPowerSaveMode(Z)Z
Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V
Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V
Landroid/os/Process;->BLUETOOTH_UID:I
@@ -2076,27 +2075,20 @@
Lcom/android/internal/telephony/ISub;->getDefaultSubId()I
Lcom/android/internal/telephony/ISub;->setDefaultDataSubId(I)V
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCallForSubscriber(I)Z
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_answerRingingCall:I
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
-Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V
Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V
Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z
Lcom/android/internal/telephony/ITelephony;->disableLocationUpdates()V
Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z
Lcom/android/internal/telephony/ITelephony;->enableLocationUpdates()V
-Lcom/android/internal/telephony/ITelephony;->endCall()Z
-Lcom/android/internal/telephony/ITelephony;->endCallForSubscriber(I)Z
Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
Lcom/android/internal/telephony/ITelephony;->getCallState()I
Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
@@ -2108,12 +2100,8 @@
Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
Lcom/android/internal/telephony/ITelephony;->iccCloseLogicalChannel(II)Z
Lcom/android/internal/telephony/ITelephony;->iccTransmitApduLogicalChannel(IIIIIIILjava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/ITelephony;->isIdleForSubscriber(ILjava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->isRadioOnForSubscriber(ILjava/lang/String;)Z
-Lcom/android/internal/telephony/ITelephony;->isRinging(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->setRadio(Z)Z
-Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
Lcom/android/internal/telephony/ITelephony;->supplyPin(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->toggleRadioOnOff()V
Lcom/android/internal/telephony/ITelephony;->updateServiceLocation()V
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 63c583f..56ca98f 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1022,6 +1022,7 @@
android.graphics.-$$Lambda$ColorSpace$Rgb$bWzafC8vMHNuVmRuTUPEFUMlfuY
android.graphics.-$$Lambda$ColorSpace$S2rlqJvkXGTpUF6mZhvkElds8JE
android.graphics.BaseCanvas
+android.graphics.BaseRecordingCanvas
android.graphics.Bitmap
android.graphics.Bitmap$1
android.graphics.Bitmap$2
@@ -2381,7 +2382,6 @@
android.os.-$$Lambda$StrictMode$yZJXPvy2veRNA-xL_SWdXzX_OLg
android.os.-$$Lambda$Trace$2zLZ-Lc2kAXsVjw_nLYeNhqmGq0
android.os.AsyncResult
-android.os.AsyncTask
android.os.AsyncTask$1
android.os.AsyncTask$2
android.os.AsyncTask$3
@@ -3304,7 +3304,6 @@
android.view.OrientationEventListener$SensorEventListenerImpl
android.view.PointerIcon
android.view.PointerIcon$1
-android.view.RecordingCanvas
android.view.RenderNode
android.view.RenderNode$NoImagePreloadHolder
android.view.RenderNodeAnimator
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist
index 8b8d640..eca3bf3 100644
--- a/config/preloaded-classes-blacklist
+++ b/config/preloaded-classes-blacklist
@@ -1,4 +1,5 @@
android.net.ConnectivityThread$Singleton
+android.os.AsyncTask
android.os.FileObserver
android.widget.Magnifier
sun.nio.fs.UnixChannelFactory
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2ee266d..482ef2d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -394,7 +394,7 @@
* <td>The final call you receive before your
* activity is destroyed. This can happen either because the
* activity is finishing (someone called {@link Activity#finish} on
- * it, or because the system is temporarily destroying this
+ * it), or because the system is temporarily destroying this
* instance of the activity to save space. You can distinguish
* between these two scenarios with the {@link
* Activity#isFinishing} method.</td>
@@ -1588,11 +1588,13 @@
* @param outState The bundle to save the state to.
*/
final void performSaveInstanceState(@NonNull Bundle outState) {
+ getApplication().dispatchActivityPreSaveInstanceState(this, outState);
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
+ getApplication().dispatchActivityPostSaveInstanceState(this, outState);
}
/**
@@ -1606,11 +1608,13 @@
*/
final void performSaveInstanceState(@NonNull Bundle outState,
@NonNull PersistableBundle outPersistentState) {
+ getApplication().dispatchActivityPreSaveInstanceState(this, outState);
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
", " + outPersistentState);
+ getApplication().dispatchActivityPostSaveInstanceState(this, outState);
}
/**
@@ -1981,7 +1985,7 @@
/**
* Perform any final cleanup before an activity is destroyed. This can
* happen either because the activity is finishing (someone called
- * {@link #finish} on it, or because the system is temporarily destroying
+ * {@link #finish} on it), or because the system is temporarily destroying
* this instance of the activity to save space. You can distinguish
* between these two scenarios with the {@link #isFinishing} method.
*
@@ -7195,6 +7199,7 @@
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+ getApplication().dispatchActivityPreCreated(this, icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
@@ -7209,6 +7214,7 @@
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
+ getApplication().dispatchActivityPostCreated(this, icicle);
}
final void performNewIntent(@NonNull Intent intent) {
@@ -7217,6 +7223,7 @@
}
final void performStart(String reason) {
+ getApplication().dispatchActivityPreStarted(this);
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
@@ -7284,6 +7291,7 @@
}
mActivityTransitionState.enterReady(this);
+ getApplication().dispatchActivityPostStarted(this);
}
/**
@@ -7338,6 +7346,7 @@
}
final void performResume(boolean followedByPause, String reason) {
+ getApplication().dispatchActivityPreResumed(this);
performRestart(true /* start */, reason);
mFragments.execPendingActions();
@@ -7387,9 +7396,11 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPostResume()");
}
+ getApplication().dispatchActivityPostResumed(this);
}
final void performPause() {
+ getApplication().dispatchActivityPrePaused(this);
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
@@ -7402,6 +7413,7 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
+ getApplication().dispatchActivityPostPaused(this);
}
final void performUserLeaving() {
@@ -7417,6 +7429,7 @@
mCanEnterPictureInPicture = false;
if (!mStopped) {
+ getApplication().dispatchActivityPreStopped(this);
if (mWindow != null) {
mWindow.closeAllPanels();
}
@@ -7451,11 +7464,13 @@
}
mStopped = true;
+ getApplication().dispatchActivityPostStopped(this);
}
mResumed = false;
}
final void performDestroy() {
+ getApplication().dispatchActivityPreDestroyed(this);
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
@@ -7465,6 +7480,7 @@
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
+ getApplication().dispatchActivityPostDestroyed(this);
}
final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e86a49d..14b8ae4 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -776,7 +776,7 @@
/** @hide */
public int getPackageScreenCompatMode(String packageName) {
try {
- return getService().getPackageScreenCompatMode(packageName);
+ return getTaskService().getPackageScreenCompatMode(packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -785,7 +785,7 @@
/** @hide */
public void setPackageScreenCompatMode(String packageName, int mode) {
try {
- getService().setPackageScreenCompatMode(packageName, mode);
+ getTaskService().setPackageScreenCompatMode(packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -794,7 +794,7 @@
/** @hide */
public boolean getPackageAskScreenCompat(String packageName) {
try {
- return getService().getPackageAskScreenCompat(packageName);
+ return getTaskService().getPackageAskScreenCompat(packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -803,7 +803,7 @@
/** @hide */
public void setPackageAskScreenCompat(String packageName, boolean ask) {
try {
- getService().setPackageAskScreenCompat(packageName, ask);
+ getTaskService().setPackageAskScreenCompat(packageName, ask);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3379,7 +3379,7 @@
*/
public ConfigurationInfo getDeviceConfigurationInfo() {
try {
- return getService().getDeviceConfigurationInfo();
+ return getTaskService().getDeviceConfigurationInfo();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 87366db..294a3ec 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,11 +18,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.TransactionTooLargeException;
import android.view.RemoteAnimationAdapter;
import java.util.ArrayList;
@@ -183,12 +187,6 @@
/** Trims memory usage in the system by removing/stopping unused application processes. */
public abstract void trimApplications();
- /** Returns the screen compatibility mode for the given application. */
- public abstract int getPackageScreenCompatMode(ApplicationInfo ai);
-
- /** Sets the screen compatibility mode for the given application. */
- public abstract void setPackageScreenCompatMode(ApplicationInfo ai, int mode);
-
/** Closes all system dialogs. */
public abstract void closeSystemDialogs(String reason);
@@ -201,6 +199,11 @@
public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
public abstract void updateOomAdj();
+ public abstract void updateCpuStats();
+ public abstract void updateUsageStats(
+ ComponentName activity, int uid, int userId, boolean resumed);
+ public abstract void updateForegroundTimeIfOnBattery(
+ String packageName, int uid, long cpuTimeDiff);
public abstract void sendForegroundProfileChanged(int userId);
/**
@@ -226,4 +229,20 @@
/** Gets the task id for a given activity. */
public abstract int getTaskIdForActivity(@NonNull IBinder token, boolean onlyRoot);
+
+ public abstract void setBooting(boolean booting);
+ public abstract boolean isBooting();
+ public abstract void setBooted(boolean booted);
+ public abstract boolean isBooted();
+ public abstract void finishBooting();
+
+ public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
+ long duration, String tag);
+ public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent,
+ String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
+ Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
+ boolean sticky, int userId);
+ public abstract ComponentName startServiceInPackage(int uid, Intent service,
+ String resolvedType, boolean fgRequired, String callingPackage, int userId)
+ throws TransactionTooLargeException;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 31e9a2d..6754df9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1541,7 +1541,7 @@
public void scheduleTrimMemory(int level) {
final Runnable r = PooledLambda.obtainRunnable(ActivityThread::handleTrimMemory,
- ActivityThread.this, level);
+ ActivityThread.this, level).recycleOnUse();
// Schedule trimming memory after drawing the frame to minimize jank-risk.
Choreographer choreographer = Choreographer.getMainThreadInstance();
if (choreographer != null) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index fd92174..9c47e79 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -429,8 +429,8 @@
/** @hide */
@UnsupportedAppUsage
public static final int OP_BLUETOOTH_SCAN = 77;
- /** @hide Use the face authentication API. */
- public static final int OP_USE_FACE = 78;
+ /** @hide Use the BiometricPrompt/BiometricManager APIs. */
+ public static final int OP_USE_BIOMETRIC = 78;
/** @hide */
@UnsupportedAppUsage
public static final int _NUM_OP = 79;
@@ -678,8 +678,8 @@
/** @hide */
public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
- /** @hide Use the face authentication API. */
- public static final String OPSTR_USE_FACE = "android:use_face";
+ /** @hide Use the BiometricPrompt/BiometricManager APIs. */
+ public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -818,7 +818,7 @@
OP_MANAGE_IPSEC_TUNNELS, // MANAGE_IPSEC_HANDOVERS
OP_START_FOREGROUND, // START_FOREGROUND
OP_COARSE_LOCATION, // BLUETOOTH_SCAN
- OP_USE_FACE, // FACE
+ OP_USE_BIOMETRIC, // BIOMETRIC
};
/**
@@ -903,7 +903,7 @@
OPSTR_MANAGE_IPSEC_TUNNELS,
OPSTR_START_FOREGROUND,
OPSTR_BLUETOOTH_SCAN,
- OPSTR_USE_FACE,
+ OPSTR_USE_BIOMETRIC,
};
/**
@@ -989,7 +989,7 @@
"MANAGE_IPSEC_TUNNELS",
"START_FOREGROUND",
"BLUETOOTH_SCAN",
- "USE_FACE",
+ "USE_BIOMETRIC",
};
/**
@@ -1163,7 +1163,7 @@
null, // MANAGE_IPSEC_TUNNELS
null, // START_FOREGROUND
null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
- null, // USE_FACE
+ null, // USE_BIOMETRIC
};
/**
@@ -1249,7 +1249,7 @@
false, // MANAGE_IPSEC_HANDOVERS
false, // START_FOREGROUND
true, // BLUETOOTH_SCAN
- false, // USE_FACE
+ false, // USE_BIOMETRIC
};
/**
@@ -1334,7 +1334,7 @@
AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
AppOpsManager.MODE_ALLOWED, // OP_START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_SCAN
- AppOpsManager.MODE_ALLOWED, // USE_FACE
+ AppOpsManager.MODE_ALLOWED, // USE_BIOMETRIC
};
/**
@@ -1423,7 +1423,7 @@
false, // MANAGE_IPSEC_TUNNELS
false, // START_FOREGROUND
false, // BLUETOOTH_SCAN
- false, // USE_FACE
+ false, // USE_BIOMETRIC
};
/**
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 68b745d..e12942f 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -65,13 +65,144 @@
public LoadedApk mLoadedApk;
public interface ActivityLifecycleCallbacks {
+
+ /**
+ * Called as the first step of the Activity being created. This is always called before
+ * {@link Activity#onCreate}.
+ */
+ default void onActivityPreCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ }
+
+ /**
+ * Called when the Activity calls {@link Activity#onCreate super.onCreate()}.
+ */
void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
+
+ /**
+ * Called as the last step of the Activity being created. This is always called after
+ * {@link Activity#onCreate}.
+ */
+ default void onActivityPostCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ }
+
+ /**
+ * Called as the first step of the Activity being started. This is always called before
+ * {@link Activity#onStart}.
+ */
+ default void onActivityPreStarted(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called when the Activity calls {@link Activity#onStart super.onStart()}.
+ */
void onActivityStarted(@NonNull Activity activity);
+
+ /**
+ * Called as the last step of the Activity being started. This is always called after
+ * {@link Activity#onStart}.
+ */
+ default void onActivityPostStarted(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called as the first step of the Activity being resumed. This is always called before
+ * {@link Activity#onResume}.
+ */
+ default void onActivityPreResumed(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called when the Activity calls {@link Activity#onResume super.onResume()}.
+ */
void onActivityResumed(@NonNull Activity activity);
+
+ /**
+ * Called as the last step of the Activity being resumed. This is always called after
+ * {@link Activity#onResume} and {@link Activity#onPostResume}.
+ */
+ default void onActivityPostResumed(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called as the first step of the Activity being paused. This is always called before
+ * {@link Activity#onPause}.
+ */
+ default void onActivityPrePaused(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called when the Activity calls {@link Activity#onPause super.onPause()}.
+ */
void onActivityPaused(@NonNull Activity activity);
+
+ /**
+ * Called as the last step of the Activity being paused. This is always called after
+ * {@link Activity#onPause}.
+ */
+ default void onActivityPostPaused(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called as the first step of the Activity being stopped. This is always called before
+ * {@link Activity#onStop}.
+ */
+ default void onActivityPreStopped(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called when the Activity calls {@link Activity#onStop super.onStop()}.
+ */
void onActivityStopped(@NonNull Activity activity);
+
+ /**
+ * Called as the last step of the Activity being stopped. This is always called after
+ * {@link Activity#onStop}.
+ */
+ default void onActivityPostStopped(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called as the first step of the Activity saving its instance state. This is always
+ * called before {@link Activity#onSaveInstanceState}.
+ */
+ default void onActivityPreSaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
+ }
+
+ /**
+ * Called when the Activity calls
+ * {@link Activity#onSaveInstanceState super.onSaveInstanceState()}.
+ */
void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState);
+
+ /**
+ * Called as the last step of the Activity saving its instance state. This is always
+ * called after{@link Activity#onSaveInstanceState}.
+ */
+ default void onActivityPostSaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
+ }
+
+ /**
+ * Called as the first step of the Activity being destroyed. This is always called before
+ * {@link Activity#onDestroy}.
+ */
+ default void onActivityPreDestroyed(@NonNull Activity activity) {
+ }
+
+ /**
+ * Called when the Activity calls {@link Activity#onDestroy super.onDestroy()}.
+ */
void onActivityDestroyed(@NonNull Activity activity);
+
+ /**
+ * Called as the last step of the Activity being destroyed. This is always called after
+ * {@link Activity#onDestroy}.
+ */
+ default void onActivityPostDestroyed(@NonNull Activity activity) {
+ }
}
/**
@@ -222,6 +353,18 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPreCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(activity,
+ savedInstanceState);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivityCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
Object[] callbacks = collectActivityLifecycleCallbacks();
@@ -234,6 +377,28 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPostCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(activity,
+ savedInstanceState);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPreStarted(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivityStarted(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
@@ -244,6 +409,26 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPostStarted(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostStarted(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPreResumed(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivityResumed(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
@@ -254,6 +439,26 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPostResumed(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPrePaused(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivityPaused(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
@@ -264,6 +469,26 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPostPaused(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPreStopped(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivityStopped(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
@@ -274,6 +499,28 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPostStopped(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostStopped(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPreSaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreSaveInstanceState(
+ activity, outState);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivitySaveInstanceState(@NonNull Activity activity,
@NonNull Bundle outState) {
Object[] callbacks = collectActivityLifecycleCallbacks();
@@ -286,6 +533,28 @@
}
@UnsupportedAppUsage
+ /* package */ void dispatchActivityPostSaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostSaveInstanceState(
+ activity, outState);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPreDestroyed(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreDestroyed(activity);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
/* package */ void dispatchActivityDestroyed(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
@@ -295,6 +564,16 @@
}
}
+ @UnsupportedAppUsage
+ /* package */ void dispatchActivityPostDestroyed(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostDestroyed(activity);
+ }
+ }
+ }
+
private Object[] collectComponentCallbacks() {
Object[] callbacks = null;
synchronized (mComponentCallbacks) {
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 37a05f0..9d82ffa 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -239,7 +239,7 @@
* @param year the selected year
* @param month the selected month (0-11 for compatibility with
* {@link Calendar#MONTH})
- * @param dayOfMonth th selected day of the month (1-31, depending on
+ * @param dayOfMonth the selected day of the month (1-31, depending on
* month)
*/
void onDateSet(DatePicker view, int year, int month, int dayOfMonth);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 16360b3..519a274 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -208,8 +208,6 @@
List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags);
// Retrieve running application processes in the system
List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
- // Get device configuration
- ConfigurationInfo getDeviceConfigurationInfo();
IBinder peekService(in Intent service, in String resolvedType, in String callingPackage);
// Turn on/off profiling in a particular process.
boolean profileControl(in String process, int userId, boolean start,
@@ -248,10 +246,7 @@
boolean runGc, in String path, in ParcelFileDescriptor fd,
in RemoteCallback finishCallback);
boolean isUserRunning(int userid, int flags);
- int getPackageScreenCompatMode(in String packageName);
void setPackageScreenCompatMode(in String packageName, int mode);
- boolean getPackageAskScreenCompat(in String packageName);
- void setPackageAskScreenCompat(in String packageName, boolean ask);
boolean switchUser(int userid);
boolean removeTask(int taskId);
void registerProcessObserver(in IProcessObserver observer);
@@ -263,7 +258,9 @@
void killAllBackgroundProcesses();
ContentProviderHolder getContentProviderExternal(in String name, int userId,
in IBinder token, String tag);
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
void removeContentProviderExternal(in String name, in IBinder token);
+ void removeContentProviderExternalAsUser(in String name, in IBinder token, int userId);
// Get memory information about the calling process.
void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
boolean killProcessesBelowForeground(in String reason);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 46664c6..b7b6352 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -344,6 +344,9 @@
void notifyPinnedStackAnimationStarted();
void notifyPinnedStackAnimationEnded();
+ // Get device configuration
+ ConfigurationInfo getDeviceConfigurationInfo();
+
/**
* Resizes the pinned stack.
*
@@ -422,4 +425,9 @@
void resumeAppSwitches();
void setActivityController(in IActivityController watcher, boolean imAMonkey);
void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake);
+
+ int getPackageScreenCompatMode(in String packageName);
+ void setPackageScreenCompatMode(in String packageName, int mode);
+ boolean getPackageAskScreenCompat(in String packageName);
+ void setPackageAskScreenCompat(in String packageName, boolean ask);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/core/java/android/app/ISmsAppService.aidl
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
rename to core/java/android/app/ISmsAppService.aidl
index c50d6d6..1ac2ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
+++ b/core/java/android/app/ISmsAppService.aidl
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -11,15 +11,13 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.stackdivider.events;
-
-import com.android.systemui.recents.events.EventBus;
+package android.app;
/**
- * Sent when the divider isn't draging anymore.
+ * @hide
*/
-public class StoppedDragingEvent extends EventBus.Event {
+interface ISmsAppService {
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3638bc4..81df447 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4440,7 +4440,7 @@
}
private CharSequence processTextSpans(CharSequence text) {
- if (hasForegroundColor()) {
+ if (hasForegroundColor() || mInNightMode) {
return ContrastColorUtil.clearColorSpans(text);
}
return text;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9f819b9..07a8504 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1636,11 +1636,14 @@
try {
final ParceledListSlice<StatusBarNotification> parceledList
= service.getAppActiveNotifications(pkg, mContext.getUserId());
- final List<StatusBarNotification> list = parceledList.getList();
- return list.toArray(new StatusBarNotification[list.size()]);
+ if (parceledList != null) {
+ final List<StatusBarNotification> list = parceledList.getList();
+ return list.toArray(new StatusBarNotification[list.size()]);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ return new StatusBarNotification[0];
}
/**
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index 39db16d..9bfdae0 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -23,19 +23,20 @@
* The memory stats for a process.
* {@hide}
*/
-public class ProcessMemoryState implements Parcelable {
- public int uid;
- public String processName;
- public int oomScore;
- public long pgfault;
- public long pgmajfault;
- public long rssInBytes;
- public long cacheInBytes;
- public long swapInBytes;
+public final class ProcessMemoryState implements Parcelable {
+ public final int uid;
+ public final String processName;
+ public final int oomScore;
+ public final long pgfault;
+ public final long pgmajfault;
+ public final long rssInBytes;
+ public final long cacheInBytes;
+ public final long swapInBytes;
+ public final long rssHighWatermarkInBytes;
public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
long pgmajfault, long rssInBytes, long cacheInBytes,
- long swapInBytes) {
+ long swapInBytes, long rssHighWatermarkInBytes) {
this.uid = uid;
this.processName = processName;
this.oomScore = oomScore;
@@ -44,6 +45,7 @@
this.rssInBytes = rssInBytes;
this.cacheInBytes = cacheInBytes;
this.swapInBytes = swapInBytes;
+ this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
}
private ProcessMemoryState(Parcel in) {
@@ -55,6 +57,7 @@
rssInBytes = in.readLong();
cacheInBytes = in.readLong();
swapInBytes = in.readLong();
+ rssHighWatermarkInBytes = in.readLong();
}
public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
@@ -84,5 +87,6 @@
parcel.writeLong(rssInBytes);
parcel.writeLong(cacheInBytes);
parcel.writeLong(swapInBytes);
+ parcel.writeLong(rssHighWatermarkInBytes);
}
}
diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/app/SmsAppService.java
new file mode 100644
index 0000000..3f2b025
--- /dev/null
+++ b/core/java/android/app/SmsAppService.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 android.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * If the default SMS app has a service that extends this class, the system always tries to bind
+ * it so that the process is always running, which allows the app to have a persistent connection
+ * to the server.
+ *
+ * <p>The service must have {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
+ * action in the intent handler, and be protected with
+ * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to
+ * be exported.
+ *
+ * <p>Apps can use
+ * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)}
+ * to disable/enable the service. Apps should use it to disable the service when it no longer needs
+ * to be running.
+ *
+ * <p>When the owner process crashes, the service will be re-bound automatically after a
+ * back-off.
+ *
+ * <p>Note the process may still be killed if the system is under heavy memory pressure, in which
+ * case the process will be re-started later.
+ */
+public class SmsAppService extends Service {
+ private final ISmsAppService mImpl;
+
+ public SmsAppService() {
+ mImpl = new ISmsAppServiceImpl();
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return mImpl.asBinder();
+ }
+
+ private class ISmsAppServiceImpl extends ISmsAppService.Stub {
+ }
+}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 2718bfa..bf3d885 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -208,10 +209,11 @@
}
/**
- * Expand the settings panel and open a subPanel, pass null to just open the settings panel.
+ * Expand the settings panel and open a subPanel. If the subpanel is null or does not have a
+ * corresponding tile, the QS panel is simply expanded
*/
@UnsupportedAppUsage
- public void expandSettingsPanel(String subPanel) {
+ public void expandSettingsPanel(@Nullable String subPanel) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 003f364..0044005 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -53,6 +53,8 @@
import android.hardware.SensorManager;
import android.hardware.SerialManager;
import android.hardware.SystemSensorManager;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.IBiometricService;
import android.hardware.camera2.CameraManager;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
@@ -132,6 +134,7 @@
import android.os.Vibrator;
import android.os.health.SystemHealthManager;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.print.IPrintManager;
import android.print.PrintManager;
import android.service.oemlock.IOemLockService;
@@ -378,7 +381,7 @@
new StaticServiceFetcher<InputMethodManager>() {
@Override
public InputMethodManager createService() {
- return InputMethodManager.getInstance();
+ return InputMethodManager.getInstanceInternal();
}});
registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class,
@@ -818,6 +821,19 @@
}
});
+ registerService(Context.BIOMETRIC_SERVICE, BiometricManager.class,
+ new CachedServiceFetcher<BiometricManager>() {
+ @Override
+ public BiometricManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ final IBinder binder =
+ ServiceManager.getServiceOrThrow(Context.BIOMETRIC_SERVICE);
+ final IBiometricService service =
+ IBiometricService.Stub.asInterface(binder);
+ return new BiometricManager(ctx.getOuterContext(), service);
+ }
+ });
+
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
new CachedServiceFetcher<TvInputManager>() {
@Override
@@ -1049,6 +1065,13 @@
throws ServiceNotFoundException {
return new TimeZoneDetector();
}});
+
+ registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
+ new CachedServiceFetcher<PermissionManager>() {
+ @Override
+ public PermissionManager createService(ContextImpl ctx) {
+ return new PermissionManager(ctx.getOuterContext());
+ }});
}
/**
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index c0903b6..5a25f5a 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1197,10 +1197,9 @@
}
if (listener != null) {
// Calling out only without a lock held.
- mLocalCallbackHandler.post(PooledLambda.obtainRunnable(
+ mLocalCallbackHandler.sendMessage(PooledLambda.obtainMessage(
OnAccessibilityEventListener::onAccessibilityEvent,
- listener, AccessibilityEvent.obtain(event))
- .recycleOnUse());
+ listener, AccessibilityEvent.obtain(event)));
}
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4f172a4..a554882 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -25,7 +25,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -214,7 +213,7 @@
}
/**
- * Sets the night mode.
+ * Sets the system-wide night mode.
* <p>
* The mode can be one of:
* <ul>
@@ -231,6 +230,12 @@
* are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
* or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
* device. Starting in API 23, changes to night mode are always effective.
+ * <p>
+ * Changes to night mode take effect globally and will result in a configuration change
+ * (and potentially an Activity lifecycle event) being applied to all running apps.
+ * Developers interested in an app-local implementation of night mode should consider using
+ * {@link android.support.v7.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the
+ * -night qualifier locally.
*
* @param mode the night mode to set
* @see #getNightMode()
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index 898d0ca..5baf2e2 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -28,10 +28,10 @@
* @hide
*/
public class WaitResult implements Parcelable {
+ public static final int INVALID_DELAY = -1;
public int result;
public boolean timeout;
public ComponentName who;
- public long thisTime;
public long totalTime;
public WaitResult() {
@@ -47,7 +47,6 @@
dest.writeInt(result);
dest.writeInt(timeout ? 1 : 0);
ComponentName.writeToParcel(who, dest);
- dest.writeLong(thisTime);
dest.writeLong(totalTime);
}
@@ -68,7 +67,6 @@
result = source.readInt();
timeout = source.readInt() != 0;
who = ComponentName.readFromParcel(source);
- thisTime = source.readLong();
totalTime = source.readLong();
}
@@ -77,7 +75,6 @@
pw.println(prefix + " result=" + result);
pw.println(prefix + " timeout=" + timeout);
pw.println(prefix + " who=" + who);
- pw.println(prefix + " thisTime=" + thisTime);
pw.println(prefix + " totalTime=" + totalTime);
}
}
\ No newline at end of file
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index e6fb5dc..096c7aa 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -28,9 +28,13 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
import android.view.DisplayInfo;
+import java.io.IOException;
+
/**
* Class that contains windowing configuration/state for other objects that contain windows directly
* or indirectly. E.g. Activities, Task, Displays, ...
@@ -511,6 +515,38 @@
}
/**
+ * Read from a protocol buffer input stream.
+ * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
+ *
+ * @param proto Stream to read the WindowConfiguration object from.
+ * @param fieldId Field Id of the WindowConfiguration as defined in the parent message
+ * @hide
+ */
+ public void readFromProto(ProtoInputStream proto, long fieldId)
+ throws IOException, WireTypeMismatchException {
+ final long token = proto.start(fieldId);
+ try {
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) APP_BOUNDS:
+ mAppBounds = new Rect();
+ mAppBounds.readFromProto(proto, APP_BOUNDS);
+ break;
+ case (int) WINDOWING_MODE:
+ mWindowingMode = proto.readInt(WINDOWING_MODE);
+ break;
+ case (int) ACTIVITY_TYPE:
+ mActivityType = proto.readInt(ACTIVITY_TYPE);
+ break;
+ }
+ }
+ } finally {
+ // Let caller handle any exceptions
+ proto.end(token);
+ }
+ }
+
+ /**
* Returns true if the activities associated with this window configuration display a shadow
* around their border.
* @hide
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bd7a2dd..1839263 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3450,15 +3450,14 @@
* @param flags Bit mask of additional options: currently supported flags are
* {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
* @param reason a string that contains the reason for wiping data, which can be
- * presented to the user.
+ * presented to the user. If the string is null or empty, user won't be notified.
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
* @throws IllegalArgumentException if the input reason string is null or empty.
*/
- public void wipeData(int flags, @NonNull CharSequence reason) {
+ public void wipeData(int flags, CharSequence reason) {
throwIfParentInstance("wipeData");
- Preconditions.checkNotNull(reason, "CharSequence is null");
- wipeDataInternal(flags, reason.toString());
+ wipeDataInternal(flags, reason != null ? reason.toString() : null);
}
/**
@@ -3469,7 +3468,7 @@
* @see #wipeData(int, CharSequence)
* @hide
*/
- private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
+ private void wipeDataInternal(int flags, String wipeReasonForUser) {
if (mService != null) {
try {
mService.wipeDataWithReason(flags, wipeReasonForUser);
@@ -7405,6 +7404,10 @@
* If any app targeting {@link android.os.Build.VERSION_CODES#O} or higher calls this method
* with {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS},
* an {@link UnsupportedOperationException} is thrown.
+ *
+ * Starting from Android Q, the device and profile owner can also call
+ * {@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY} to restrict unknown sources for
+ * all users.
* </strong>
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 9f22ad1..308b39e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -165,6 +165,12 @@
*/
public static final int KEYGUARD_HIDDEN = 18;
+ /**
+ * Keep in sync with the greatest event type value.
+ * @hide
+ */
+ public static final int MAX_EVENT_TYPE = 18;
+
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -176,6 +182,12 @@
public @interface EventFlags {}
/**
+ * Bitwise OR all valid flag constants to create this constant.
+ * @hide
+ */
+ public static final int VALID_FLAG_BITS = FLAG_IS_PACKAGE_INSTANT_APP;
+
+ /**
* {@hide}
*/
@UnsupportedAppUsage
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 183be5f..559a59b 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -73,6 +73,8 @@
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
+ private static final int UPLOADING_FEATURE_BITMASK = 0x08;
+
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
@@ -395,6 +397,23 @@
return false;
}
+ /**
+ * Returns the "Uploading" feature bit value from the SDP record's
+ * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114).
+ * @param device The Bluetooth device to get this value for.
+ * @return Returns true if the Uploading bit value in SDP record's
+ * MapSupportedFeatures field is set. False is returned otherwise.
+ */
+ public boolean isUploadingSupported(BluetoothDevice device) {
+ try {
+ return (mService != null && isEnabled() && isValidDevice(device))
+ && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ return false;
+ }
+
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 3fe17840..8760efe 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -95,9 +95,6 @@
* Sets the current primary clip on the clipboard. This is the clip that
* is involved in normal cut and paste operations.
*
- * <em>If the application is not the default IME or does not have input focus this will have
- * no effect.</em>
- *
* @param clip The clipped data item to set.
* @see #getPrimaryClip()
* @see #clearPrimaryClip()
@@ -115,9 +112,6 @@
/**
* Clears any current primary clip on the clipboard.
*
- * <em>If the application is not the default IME or does not have input focus this will have
- * no effect.</em>
- *
* @see #setPrimaryClip(ClipData)
*/
public void clearPrimaryClip() {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index bd1e6a4..a64eead 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -51,6 +51,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -58,6 +60,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Objects;
/**
* Content providers are one of the primary building blocks of Android applications, providing
@@ -99,6 +102,7 @@
* <p>For more information about using content providers, read the
* <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
* developer guide.</p>
+ * </div>
*/
public abstract class ContentProvider implements ComponentCallbacks2 {
@@ -220,7 +224,7 @@
@Override
public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
@@ -268,7 +272,7 @@
@Override
public String getType(Uri uri) {
// getCallingPackage() isn't available in getType(), as the javadoc states.
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "getType");
try {
@@ -280,7 +284,7 @@
@Override
public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -303,7 +307,7 @@
@Override
public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -327,11 +331,12 @@
for (int i = 0; i < numOperations; i++) {
ContentProviderOperation operation = operations.get(i);
Uri uri = operation.getUri();
- validateIncomingUri(uri);
userIds[i] = getUserIdFromUri(uri);
- if (userIds[i] != UserHandle.USER_CURRENT) {
- // Removing the user id from the uri.
- operation = new ContentProviderOperation(operation, true);
+ uri = validateIncomingUri(uri);
+ uri = maybeGetUriWithoutUserId(uri);
+ // Rebuild operation if we changed the Uri above
+ if (!Objects.equals(operation.getUri(), uri)) {
+ operation = new ContentProviderOperation(operation, uri);
operations.set(i, operation);
}
if (operation.isReadOperation()) {
@@ -368,7 +373,7 @@
@Override
public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -386,7 +391,7 @@
@Override
public int update(String callingPkg, Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -405,7 +410,7 @@
public ParcelFileDescriptor openFile(
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
IBinder callerToken) throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, mode, callerToken);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
@@ -423,7 +428,7 @@
public AssetFileDescriptor openAssetFile(
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, mode, null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
@@ -454,7 +459,7 @@
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
// getCallingPackage() isn't available in getType(), as the javadoc states.
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "getStreamTypes");
try {
@@ -468,7 +473,7 @@
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, "r", null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
@@ -489,7 +494,7 @@
@Override
public Uri canonicalize(String callingPkg, Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -507,7 +512,7 @@
@Override
public Uri uncanonicalize(String callingPkg, Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -526,7 +531,7 @@
@Override
public boolean refresh(String callingPkg, Uri uri, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return false;
@@ -1965,7 +1970,7 @@
*/
if (mContext == null) {
mContext = context;
- if (context != null) {
+ if (context != null && mTransport != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
@@ -2074,7 +2079,8 @@
}
/** @hide */
- private void validateIncomingUri(Uri uri) throws SecurityException {
+ @VisibleForTesting
+ public Uri validateIncomingUri(Uri uri) throws SecurityException {
String auth = uri.getAuthority();
if (!mSingleUser) {
int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
@@ -2093,6 +2099,15 @@
}
throw new SecurityException(message);
}
+
+ // Normalize the path by removing any empty path segments, which can be
+ // a source of security issues.
+ final String encodedPath = uri.getEncodedPath();
+ if (encodedPath != null && encodedPath.indexOf("//") != -1) {
+ return uri.buildUpon().encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ } else {
+ return uri;
+ }
}
/** @hide */
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index e3d9b19..7dc4577 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -101,13 +101,9 @@
}
/** @hide */
- public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
+ public ContentProviderOperation(ContentProviderOperation cpo, Uri withUri) {
mType = cpo.mType;
- if (removeUserIdFromUri) {
- mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
- } else {
- mUri = cpo.mUri;
- }
+ mUri = withUri;
mValues = cpo.mValues;
mSelection = cpo.mSelection;
mSelectionArgs = cpo.mSelectionArgs;
@@ -117,14 +113,6 @@
mYieldAllowed = cpo.mYieldAllowed;
}
- /** @hide */
- public ContentProviderOperation getWithoutUserIdInUri() {
- if (ContentProvider.uriHasUserId(mUri)) {
- return new ContentProviderOperation(this, true);
- }
- return this;
- }
-
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
Uri.writeToParcel(dest, mUri);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2ae3ae6..d711574 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3031,6 +3031,7 @@
AUDIO_SERVICE,
FINGERPRINT_SERVICE,
//@hide: FACE_SERVICE,
+ BIOMETRIC_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -3087,6 +3088,7 @@
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
+ PERMISSION_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3167,11 +3169,11 @@
*
* <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
* don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
- * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
- * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
- * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
- * Generally, if you are running as an instant app you should always check whether the result
- * of this method is null.
+ * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+ * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+ * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+ * return <code>null</code>. Generally, if you are running as an instant app you should always
+ * check whether the result of this method is null.
*
* @param name The name of the desired service.
*
@@ -3258,11 +3260,11 @@
*
* <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
* don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
- * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
- * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
- * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
- * Generally, if you are running as an instant app you should always check whether the result
- * of this method is null.
+ * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+ * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+ * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+ * return <code>null</code>. Generally, if you are running as an instant app you should always
+ * check whether the result of this method is null.
*
* @param serviceClass The class of the desired service.
* @return The service or null if the class is not a supported system service.
@@ -3681,15 +3683,6 @@
public static final String AUDIO_SERVICE = "audio";
/**
- * Use with {@link #getSystemService(String)}
- *
- * @hide
- * @see #getSystemService(String)
- * @see com.android.server.biometrics.BiometricPromptService
- */
- public static final String BIOMETRIC_PROMPT_SERVICE = "biometric_prompt";
-
- /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
@@ -3701,7 +3694,6 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * Use with {@link #getSystemService} to retrieve a
* {@link android.hardware.face.FaceManager} for handling management
* of face authentication.
*
@@ -3712,6 +3704,16 @@
public static final String FACE_SERVICE = "face";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.hardware.biometrics.BiometricManager} for handling management
+ * of face authentication.
+ *
+ * @see #getSystemService
+ * @see android.hardware.biometrics.BiometricManager
+ */
+ public static final String BIOMETRIC_SERVICE = "biometric";
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
@@ -3859,6 +3861,14 @@
*/
public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
+ /**
+ * Official published name of the (internal) permission service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String PERMISSION_SERVICE = "permission";
/**
* Use with {@link #getSystemService(String)} to retrieve an
@@ -4301,6 +4311,12 @@
public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
/**
+ * Binder service name for {@link AppBindingService}.
+ * @hide
+ */
+ public static final String APP_BINDING_SERVICE = "app_binding";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a15711f5..3032d16 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1170,6 +1170,14 @@
public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27;
/**
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package requires at least one split and it was not provided.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_MISSING_SPLIT = -28;
+
+ /**
* Installation parse return code: this is passed in the
* {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a
* file, or does not end with the expected '.apk' extension.
@@ -5927,8 +5935,8 @@
case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
- case INSTALL_FAILED_BAD_DEX_METADATA:
- return "INSTALL_FAILED_BAD_DEX_METADATA";
+ case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
+ case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
default: return Integer.toString(status);
}
}
@@ -5979,6 +5987,7 @@
case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
case INSTALL_FAILED_NO_MATCHING_ABIS: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+ case INSTALL_FAILED_MISSING_SPLIT: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
default: return PackageInstaller.STATUS_FAILURE;
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 03a3d1f..f5431ca 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -40,7 +40,6 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -73,6 +72,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructStat;
@@ -258,19 +258,6 @@
}
}
- /** @hide */
- public static class SplitPermissionInfo {
- public final String rootPerm;
- public final String[] newPerms;
- public final int targetSdk;
-
- public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) {
- this.rootPerm = rootPerm;
- this.newPerms = newPerms;
- this.targetSdk = targetSdk;
- }
- }
-
/**
* List of new permissions that have been added since 1.0.
* NOTE: These must be declared in SDK version order, with permissions
@@ -290,34 +277,6 @@
};
/**
- * List of permissions that have been split into more granular or dependent
- * permissions.
- * @hide
- */
- public static final PackageParser.SplitPermissionInfo SPLIT_PERMISSIONS[] =
- new PackageParser.SplitPermissionInfo[] {
- // READ_EXTERNAL_STORAGE is always required when an app requests
- // WRITE_EXTERNAL_STORAGE, because we can't have an app that has
- // write access without read access. The hack here with the target
- // target SDK version ensures that this grant is always done.
- new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
- new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE },
- android.os.Build.VERSION_CODES.CUR_DEVELOPMENT+1),
- new PackageParser.SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS,
- new String[] { android.Manifest.permission.READ_CALL_LOG },
- android.os.Build.VERSION_CODES.JELLY_BEAN),
- new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS,
- new String[] { android.Manifest.permission.WRITE_CALL_LOG },
- android.os.Build.VERSION_CODES.JELLY_BEAN),
- new PackageParser.SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION,
- new String[] { android.Manifest.permission.ACCESS_BACKGROUND_LOCATION },
- android.os.Build.VERSION_CODES.P0),
- new PackageParser.SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION,
- new String[] { android.Manifest.permission.ACCESS_BACKGROUND_LOCATION },
- android.os.Build.VERSION_CODES.P0),
- };
-
- /**
* @deprecated callers should move to explicitly passing around source path.
*/
@Deprecated
@@ -493,10 +452,12 @@
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
+ public final boolean isSplitRequired;
public ApkLite(String codePath, String packageName, String splitName,
boolean isFeatureSplit,
- String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor,
+ String configForSplit, String usesSplitName, boolean isSplitRequired,
+ int versionCode, int versionCodeMajor,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
SigningDetails signingDetails, boolean coreApp,
boolean debuggable, boolean multiArch, boolean use32bitAbi,
@@ -519,6 +480,7 @@
this.use32bitAbi = use32bitAbi;
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
+ this.isSplitRequired = isSplitRequired;
}
public long getLongVersionCode() {
@@ -1736,6 +1698,7 @@
boolean extractNativeLibs = true;
boolean isolatedSplits = false;
boolean isFeatureSplit = false;
+ boolean isSplitRequired = false;
String configForSplit = null;
String usesSplitName = null;
@@ -1758,6 +1721,8 @@
configForSplit = attrs.getAttributeValue(i);
} else if (attr.equals("isFeatureSplit")) {
isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
+ } else if (attr.equals("isSplitRequired")) {
+ isSplitRequired = attrs.getAttributeBooleanValue(i, false);
}
}
@@ -1813,8 +1778,8 @@
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
- configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
- installLocation, verifiers, signingDetails, coreApp, debuggable,
+ configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
+ revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
}
@@ -2474,16 +2439,18 @@
Slog.i(TAG, implicitPerms.toString());
}
- final int NS = PackageParser.SPLIT_PERMISSIONS.length;
+
+ final int NS = PermissionManager.SPLIT_PERMISSIONS.length;
for (int is=0; is<NS; is++) {
- final PackageParser.SplitPermissionInfo spi
- = PackageParser.SPLIT_PERMISSIONS[is];
- if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
- || !pkg.requestedPermissions.contains(spi.rootPerm)) {
+ final PermissionManager.SplitPermissionInfo spi =
+ PermissionManager.SPLIT_PERMISSIONS[is];
+ if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk()
+ || !pkg.requestedPermissions.contains(spi.getRootPermission())) {
continue;
}
- for (int in=0; in<spi.newPerms.length; in++) {
- final String perm = spi.newPerms[in];
+ final String[] newPerms = spi.getNewPermissions();
+ for (int in = 0; in < newPerms.length; in++) {
+ final String perm = newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 121b432..799f8e5 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -46,6 +46,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.WindowConfiguration;
+import android.content.LocaleProto;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
import android.os.Build;
@@ -54,7 +55,9 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
import android.view.View;
import com.android.internal.util.XmlUtils;
@@ -67,6 +70,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
/**
@@ -1086,12 +1090,14 @@
/**
* Write to a protocol buffer output stream.
* Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ * Has the option to ignore fields that don't need to be persisted to disk.
*
* @param protoOutputStream Stream to write the Configuration object to.
* @param fieldId Field Id of the Configuration as defined in the parent message
+ * @param persisted Note if this proto will be persisted to disk
* @hide
*/
- public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId, boolean persisted) {
final long token = protoOutputStream.start(fieldId);
protoOutputStream.write(FONT_SCALE, fontScale);
protoOutputStream.write(MCC, mcc);
@@ -1113,13 +1119,137 @@
protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp);
protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp);
protoOutputStream.write(DENSITY_DPI, densityDpi);
- if (windowConfiguration != null) {
+ // For persistence, we do not care about window configuration
+ if (!persisted && windowConfiguration != null) {
windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION);
}
protoOutputStream.end(token);
}
/**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ *
+ * @param protoOutputStream Stream to write the Configuration object to.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ writeToProto(protoOutputStream, fieldId, false);
+ }
+
+ /**
+ * Read from a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ *
+ * @param protoInputStream Stream to read the Configuration object from.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @hide
+ */
+ public void readFromProto(ProtoInputStream protoInputStream, long fieldId) throws IOException {
+ final long token = protoInputStream.start(fieldId);
+ final List<Locale> list = new ArrayList();
+ try {
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) FONT_SCALE:
+ fontScale = protoInputStream.readFloat(FONT_SCALE);
+ break;
+ case (int) MCC:
+ mcc = protoInputStream.readInt(MCC);
+ break;
+ case (int) MNC:
+ mnc = protoInputStream.readInt(MNC);
+ break;
+ case (int) LOCALES:
+ // Parse the Locale here to handle all the repeated Locales
+ // The LocaleList will be created when the message is completed
+ final long localeToken = protoInputStream.start(LOCALES);
+ String language = "";
+ String country = "";
+ String variant = "";
+ try {
+ while (protoInputStream.nextField()
+ != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) LocaleProto.LANGUAGE:
+ language = protoInputStream.readString(
+ LocaleProto.LANGUAGE);
+ break;
+ case (int) LocaleProto.COUNTRY:
+ country = protoInputStream.readString(LocaleProto.COUNTRY);
+ break;
+ case (int) LocaleProto.VARIANT:
+ variant = protoInputStream.readString(LocaleProto.VARIANT);
+ break;
+ }
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // rethrow for caller deal with
+ throw wtme;
+ } finally {
+ protoInputStream.end(localeToken);
+ list.add(new Locale(language, country, variant));
+ }
+ break;
+ case (int) SCREEN_LAYOUT:
+ screenLayout = protoInputStream.readInt(SCREEN_LAYOUT);
+ break;
+ case (int) COLOR_MODE:
+ colorMode = protoInputStream.readInt(COLOR_MODE);
+ break;
+ case (int) TOUCHSCREEN:
+ touchscreen = protoInputStream.readInt(TOUCHSCREEN);
+ break;
+ case (int) KEYBOARD:
+ keyboard = protoInputStream.readInt(KEYBOARD);
+ break;
+ case (int) KEYBOARD_HIDDEN:
+ keyboardHidden = protoInputStream.readInt(KEYBOARD_HIDDEN);
+ break;
+ case (int) HARD_KEYBOARD_HIDDEN:
+ hardKeyboardHidden = protoInputStream.readInt(HARD_KEYBOARD_HIDDEN);
+ break;
+ case (int) NAVIGATION:
+ navigation = protoInputStream.readInt(NAVIGATION);
+ break;
+ case (int) NAVIGATION_HIDDEN:
+ navigationHidden = protoInputStream.readInt(NAVIGATION_HIDDEN);
+ break;
+ case (int) ORIENTATION:
+ orientation = protoInputStream.readInt(ORIENTATION);
+ break;
+ case (int) UI_MODE:
+ uiMode = protoInputStream.readInt(UI_MODE);
+ break;
+ case (int) SCREEN_WIDTH_DP:
+ screenWidthDp = protoInputStream.readInt(SCREEN_WIDTH_DP);
+ break;
+ case (int) SCREEN_HEIGHT_DP:
+ screenHeightDp = protoInputStream.readInt(SCREEN_HEIGHT_DP);
+ break;
+ case (int) SMALLEST_SCREEN_WIDTH_DP:
+ smallestScreenWidthDp = protoInputStream.readInt(SMALLEST_SCREEN_WIDTH_DP);
+ break;
+ case (int) DENSITY_DPI:
+ densityDpi = protoInputStream.readInt(DENSITY_DPI);
+ break;
+ case (int) WINDOW_CONFIGURATION:
+ windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION);
+ break;
+ }
+ }
+ } finally {
+ // Let caller handle any exceptions
+ if (list.size() > 0) {
+ //Create the LocaleList from the collected Locales
+ setLocales(new LocaleList(list.toArray(new Locale[list.size()])));
+ }
+ protoInputStream.end(token);
+ }
+ }
+
+ /**
* Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
* stream.
*
diff --git a/core/java/android/content/res/package.html b/core/java/android/content/res/package.html
index 3d0bac1..3970b16 100644
--- a/core/java/android/content/res/package.html
+++ b/core/java/android/content/res/package.html
@@ -1,7 +1,7 @@
<HTML>
<BODY>
<p>Contains classes for accessing application resources,
-such as raw asset files, colors, drawables, media or other other files
+such as raw asset files, colors, drawables, media, or other files
in the package, plus important device configuration details
(orientation, input types, etc.) that affect how the application may behave.</p>
@@ -9,4 +9,4 @@
href="{@docRoot}guide/topics/resources/index.html">Application Resources</a> guide.</p>
{@more}
</BODY>
-</HTML>
\ No newline at end of file
+</HTML>
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 01557c5..eb5c720 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -649,7 +649,7 @@
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful.
* @return true if the transaction was yielded
- * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
+ * @deprecated if the db is locked more than once (because of nested transactions) then the lock
* will not be yielded. Use yieldIfContendedSafely instead.
*/
@Deprecated
diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java
index 94f2ac0..0d7b695 100644
--- a/core/java/android/hardware/GeomagneticField.java
+++ b/core/java/android/hardware/GeomagneticField.java
@@ -31,7 +31,7 @@
* Android may use a newer version of the model.
*/
public class GeomagneticField {
- // The magnetic field at a given point, in nonoteslas in geodetic
+ // The magnetic field at a given point, in nanoteslas in geodetic
// coordinates.
private float mX;
private float mY;
@@ -278,7 +278,7 @@
}
/**
- * @return Horizontal component of the field strength in nonoteslas.
+ * @return Horizontal component of the field strength in nanoteslas.
*/
public float getHorizontalStrength() {
return (float) Math.hypot(mX, mY);
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
new file mode 100644
index 0000000..eea5f9b
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricManager.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 android.hardware.biometrics;
+
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
+ */
+public class BiometricManager {
+
+ private final Context mContext;
+ private final IBiometricService mService;
+
+ /**
+ * @hide
+ * @param context
+ * @param service
+ */
+ public BiometricManager(Context context, IBiometricService service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Determine if there is at least one biometric enrolled.
+ *
+ * @return true if at least one biometric is enrolled, false otherwise
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public boolean hasEnrolledBiometrics() {
+ try {
+ return mService.hasEnrolledBiometrics(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1cca27d..83998cc 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -225,7 +225,7 @@
private final IBinder mToken = new Binder();
private final Context mContext;
- private final IBiometricPromptService mService;
+ private final IBiometricService mService;
private final Bundle mBundle;
private final ButtonInfo mPositiveButtonInfo;
private final ButtonInfo mNegativeButtonInfo;
@@ -250,8 +250,8 @@
}
};
- IBiometricPromptServiceReceiver mBiometricPromptServiceReceiver =
- new IBiometricPromptServiceReceiver.Stub() {
+ IBiometricServiceReceiver mBiometricServiceReceiver =
+ new IBiometricServiceReceiver.Stub() {
@Override
public void onAuthenticationSucceeded(long deviceId) throws RemoteException {
@@ -290,8 +290,8 @@
mBundle = bundle;
mPositiveButtonInfo = positiveButtonInfo;
mNegativeButtonInfo = negativeButtonInfo;
- mService = IBiometricPromptService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_PROMPT_SERVICE));
+ mService = IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
/**
@@ -348,7 +348,6 @@
* @hide
*/
public AuthenticationResult(CryptoObject crypto) {
- // For compatibility, this extends from common base class as FingerprintManager does.
// Identifier and userId is not used for BiometricPrompt.
super(crypto, null /* identifier */, 0 /* userId */);
}
@@ -410,8 +409,8 @@
}
/**
- * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
- * scanning for a fingerprint. It terminates when {@link
+ * This call warms up the biometric hardware, displays a system-provided dialog, and starts
+ * scanning for a biometric. It terminates when {@link
* AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
* AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user
* dismisses the system-provided dialog, at which point the crypto object becomes invalid. This
@@ -453,8 +452,8 @@
}
/**
- * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
- * scanning for a fingerprint. It terminates when {@link
+ * This call warms up the biometric hardware, displays a system-provided dialog, and starts
+ * scanning for a biometric. It terminates when {@link
* AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
* AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when
* the user dismisses the system-provided dialog. This operation can be canceled by using the
@@ -516,7 +515,7 @@
mAuthenticationCallback = callback;
final long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, mContext.getUserId(),
- mBiometricPromptServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
+ mBiometricServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
mBundle, mDialogReceiver);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
similarity index 66%
rename from core/java/android/hardware/biometrics/IBiometricPromptService.aidl
rename to core/java/android/hardware/biometrics/IBiometricService.aidl
index 2c93579..fd9d572 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -18,21 +18,25 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricPromptServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiver;
/**
- * Communication channel from BiometricPrompt to BiometricPromptService. The interface does not
- * expose specific biometric modalities. The system will use the default biometric for apps. On
- * devices with more than one, the choice is dictated by user preference in Settings.
+ * Communication channel from BiometricPrompt and BiometricManager to BiometricService. The
+ * interface does not expose specific biometric modalities. The system will use the default
+ * biometric for apps. On devices with more than one, the choice is dictated by user preference in
+ * Settings.
* @hide
*/
-interface IBiometricPromptService {
+interface IBiometricService {
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
void authenticate(IBinder token, long sessionId, int userId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ IBiometricServiceReceiver receiver, int flags, String opPackageName,
in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
+
+ // Returns true if the user has at least one enrolled biometric.
+ boolean hasEnrolledBiometrics(String opPackageName);
}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
similarity index 88%
rename from core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl
rename to core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index 1ef6c52..71abdd2 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -20,10 +20,10 @@
import android.os.UserHandle;
/**
- * Communication channel from the BiometricPromptService back to BiometricPrompt.
+ * Communication channel from the BiometricService back to BiometricPrompt.
* @hide
*/
-oneway interface IBiometricPromptServiceReceiver {
+oneway interface IBiometricServiceReceiver {
void onAuthenticationSucceeded(long deviceId);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, String message);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d967fba..7810e6c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1889,7 +1889,7 @@
final long ident = Binder.clearCallingIdentity();
try {
CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable(
- CameraDeviceCallbacks::notifyError, this, code));
+ CameraDeviceCallbacks::notifyError, this, code).recycleOnUse());
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
index 9ac89c8..3b8beac 100644
--- a/core/java/android/hardware/camera2/utils/CloseableLock.java
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -290,7 +290,7 @@
/**
* Release a single lock that was acquired.
*
- * <p>Any other other that is blocked and trying to acquire a lock will get a chance
+ * <p>Any other thread that is blocked and trying to acquire a lock will get a chance
* to acquire the lock.</p>
*
* @throws IllegalStateException if no locks were acquired, or if the lock was already closed
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 20e0116..873a24a 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -244,17 +244,17 @@
}
/**
- * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
+ * Requests an auth token to tie sensitive operations to the confirmation of
* existing device credentials (e.g. pin/pattern/password).
*
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public long preEnroll() {
+ public long generateChallenge() {
long result = 0;
if (mService != null) {
try {
- result = mService.preEnroll(mToken);
+ result = mService.generateChallenge(mToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -263,16 +263,46 @@
}
/**
- * Finishes enrollment and cancels the current auth token.
+ * Invalidates the current auth token.
*
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public int postEnroll() {
+ public int revokeChallenge() {
int result = 0;
if (mService != null) {
try {
- result = mService.postEnroll(mToken);
+ result = mService.revokeChallenge(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(MANAGE_BIOMETRIC)
+ public void setRequireAttention(boolean requireAttention, byte[] token) {
+ if (mService != null) {
+ try {
+ mService.setRequireAttention(requireAttention, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(MANAGE_BIOMETRIC)
+ public boolean getRequireAttention(byte[] token) {
+ boolean result = true;
+ if (mService != null) {
+ try {
+ mService.getRequireAttention(token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -521,16 +551,16 @@
*/
public static String getErrorString(Context context, int errMsg, int vendorCode) {
switch (errMsg) {
- case FACE_ERROR_HW_UNAVAILABLE:
- return context.getString(
- com.android.internal.R.string.face_error_hw_not_available);
case FACE_ERROR_UNABLE_TO_PROCESS:
return context.getString(
com.android.internal.R.string.face_error_unable_to_process);
- case FACE_ERROR_TIMEOUT:
- return context.getString(com.android.internal.R.string.face_error_timeout);
+ case FACE_ERROR_HW_UNAVAILABLE:
+ return context.getString(
+ com.android.internal.R.string.face_error_hw_not_available);
case FACE_ERROR_NO_SPACE:
return context.getString(com.android.internal.R.string.face_error_no_space);
+ case FACE_ERROR_TIMEOUT:
+ return context.getString(com.android.internal.R.string.face_error_timeout);
case FACE_ERROR_CANCELED:
return context.getString(com.android.internal.R.string.face_error_canceled);
case FACE_ERROR_LOCKOUT:
@@ -538,6 +568,8 @@
case FACE_ERROR_LOCKOUT_PERMANENT:
return context.getString(
com.android.internal.R.string.face_error_lockout_permanent);
+ case FACE_ERROR_USER_CANCELED:
+ return context.getString(com.android.internal.R.string.face_error_user_canceled);
case FACE_ERROR_NOT_ENROLLED:
return context.getString(com.android.internal.R.string.face_error_not_enrolled);
case FACE_ERROR_HW_NOT_PRESENT:
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index dd995c9..6681bd7 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -17,7 +17,7 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricPromptServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -34,8 +34,8 @@
// This method invokes the BiometricDialog. The arguments are almost the same as above,
// but should only be called from (BiometricPromptService).
- void authenticateFromService(IBinder token, long sessionId, int userId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ 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);
@@ -66,10 +66,10 @@
boolean isHardwareDetected(long deviceId, String opPackageName);
// Get a pre-enrollment authentication token
- long preEnroll(IBinder token);
+ long generateChallenge(IBinder token);
// Finish an enrollment sequence and invalidate the authentication token
- int postEnroll(IBinder token);
+ int revokeChallenge(IBinder token);
// Determine if a user has at least one enrolled face
boolean hasEnrolledFaces(int userId, String opPackageName);
@@ -94,4 +94,8 @@
// Enumerate all faces
void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver);
+
+ int setRequireAttention(boolean requireAttention, in byte [] token);
+
+ boolean getRequireAttention(in byte [] token);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2b2c0b7..2662a11 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,7 +17,7 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricPromptServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -40,7 +40,7 @@
// called from BiometricPromptService. The additional uid, pid, userId arguments should be
// determined by BiometricPromptService.
void authenticateFromService(IBinder token, long sessionId, int userId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ IBiometricServiceReceiver receiver, int flags, String opPackageName,
in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
int callingUid, int callingPid, int callingUserId);
diff --git a/core/java/android/hardware/location/ContextHubBroadcastReceiver.java b/core/java/android/hardware/location/ContextHubBroadcastReceiver.java
new file mode 100644
index 0000000..e0cc8b7
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubBroadcastReceiver.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.location;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+
+/**
+ * A BroadcastReceiver that can be used with the Context Hub Service notifications.
+ *
+ * @hide
+ */
+public class ContextHubBroadcastReceiver extends BroadcastReceiver {
+ // The context at which this receiver operates in
+ private Context mContext;
+
+ // The handler to post callbacks to when receiving Context Hub Service intents
+ private Handler mHandler;
+
+ // The callback to be invoked when receiving Context Hub Service intents
+ private ContextHubClientCallback mCallback;
+
+ // The string to use as the broadcast action for this receiver
+ private String mAction;
+
+ // True when this receiver is registered to receive Intents, false otherwise
+ private boolean mRegistered = false;
+
+ public ContextHubBroadcastReceiver(Context context, Handler handler,
+ ContextHubClientCallback callback, String tag) {
+ mContext = context;
+ mHandler = handler;
+ mCallback = callback;
+ mAction = tag;
+ }
+
+ /**
+ * Registers this receiver to receive Intents from the Context Hub Service. This method must
+ * only be invoked when the receiver is not registered.
+ *
+ * @throws IllegalStateException if the receiver is already registered
+ */
+ public void register() throws IllegalStateException {
+ if (mRegistered) {
+ throw new IllegalStateException(
+ "Cannot register ContextHubBroadcastReceiver multiple times");
+ }
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(mAction);
+ mContext.registerReceiver(this, intentFilter, null /* broadcastPermission */, mHandler);
+ mRegistered = true;
+ }
+
+ /**
+ * Unregisters this receiver. This method must only be invoked if {@link #register()} is
+ * previously invoked.
+ *
+ * @throws IllegalStateException if the receiver is not yet registered
+ */
+ public void unregister() throws IllegalStateException {
+ if (!mRegistered) {
+ throw new IllegalStateException(
+ "Cannot unregister ContextHubBroadcastReceiver when not registered");
+ }
+ mContext.unregisterReceiver(this);
+ mRegistered = false;
+ }
+
+ /**
+ * Creates a new PendingIntent associated with this receiver.
+ *
+ * @param flags the flags {@link PendingIntent.Flags} to use for the PendingIntent
+ *
+ * @return a PendingIntent to receive notifications for this receiver
+ */
+ public PendingIntent getPendingIntent(@PendingIntent.Flags int flags) {
+ return PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent(mAction), flags);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // TODO: Implement this
+ }
+}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 2335203..917644d 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.app.PendingIntent;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
@@ -100,6 +101,57 @@
}
/**
+ * Registers to receive persistent intents for a given nanoapp.
+ *
+ * This method should be used if the caller wants to receive notifications even after the
+ * process exits. The client must have an open connection with the Context Hub Service (i.e. it
+ * cannot have been closed through the {@link #close()} method). If registered successfully,
+ * intents will be delivered regarding events for the specified nanoapp from the attached
+ * Context Hub. Any unicast messages for this client will also be delivered. The intent will
+ * have an extra {@link #EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which will
+ * contain the type of the event. See {@link ContextHubManager.Event} for description of each
+ * event type.
+ *
+ * When the intent is received, this client can be recreated through
+ * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
+ * ContextHubClientCallback, Exectutor)}. When recreated, the client 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.
+ *
+ * Intents will be delivered until it is unregistered through
+ * {@link #unregisterIntent(PendingIntent)}. Note that the registration of this client will
+ * continued to be maintained at the Context Hub Service until
+ * {@link #unregisterIntent(PendingIntent)} is called for registered intents.
+ *
+ * See {@link ContextHubBroadcastReceiver} for a helper class to generate the
+ * {@link PendingIntent} through a {@link BroadcastReceiver}, and maps an {@link Intent} to a
+ * {@link ContextHubClientCallback}.
+ *
+ * @param intent The PendingIntent to register for this client
+ * @param nanoAppId the unique ID of the nanoapp to receive events for
+ * @return true on success, false otherwise
+ *
+ * @hide
+ */
+ public boolean registerIntent(@NonNull PendingIntent intent, long nanoAppId) {
+ // TODO: Implement this
+ return false;
+ }
+
+ /**
+ * Unregisters an intent previously registered via {@link #registerIntent(PendingIntent, long)}.
+ * If this intent has not been registered for this client, this method returns false.
+ *
+ * @return true on success, false otherwise
+ *
+ * @hide
+ */
+ public boolean unregisterIntent(@NonNull PendingIntent intent) {
+ // TODO: Implement this
+ return false;
+ }
+
+ /**
* Sends a message to a nanoapp through the Context Hub Service.
*
* This function returns RESULT_SUCCESS if the message has reached the HAL, but
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 12d0531..36f3586 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -16,12 +16,14 @@
package android.hardware.location;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.PendingIntent;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -33,6 +35,8 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.concurrent.Executor;
@@ -49,6 +53,111 @@
public final class ContextHubManager {
private static final String TAG = "ContextHubManager";
+ /**
+ * 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.location.hardware.extra.NANOAPP_ID";
+
+ /**
+ * An extra of type int describing the nanoapp-specific abort code.
+ *
+ * @hide
+ */
+ public static final String EXTRA_NANOAPP_ABORT_CODE =
+ "android.location.hardware.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.location.hardware.extra.MESSAGE";
+
+ /**
+ * Constants describing the type of events from a Context Hub.
+ * {@hide}
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_NANOAPP_LOADED,
+ EVENT_NANOAPP_UNLOADED,
+ EVENT_NANOAPP_ENABLED,
+ EVENT_NANOAPP_DISABLED,
+ EVENT_NANOAPP_ABORTED,
+ EVENT_NANOAPP_MESSAGE,
+ EVENT_HUB_RESET,
+ })
+ public @interface Event { }
+
+ /**
+ * 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;
@@ -682,6 +791,57 @@
}
/**
+ * Creates a ContextHubClient based on an Intent received by the Context Hub Service.
+ *
+ * This method is intended to be used after receiving an Intent received as a result of
+ * {@link ContextHubClient.registerIntent(PendingIntent, long)}, and must have been created
+ * through {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} or
+ * equivalent at an earlier time.
+ *
+ * @param intent the intent that is associated with a client
+ * @param hubInfo the hub to attach this client to
+ * @param callback the notification callback to register
+ * @param executor the executor to invoke the callback
+ * @return the registered client object
+ *
+ * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or the intent
+ * was not associated with a client
+ * @throws IllegalStateException if there were too many registered clients at the service
+ * @throws NullPointerException if intent, hubInfo, callback, or executor is null
+ *
+ * @hide
+ */
+ @NonNull public ContextHubClient createClient(
+ @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo,
+ @NonNull ContextHubClientCallback callback,
+ @NonNull @CallbackExecutor Executor executor) {
+ // TODO: Implement this
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Equivalent to {@link #createClient(Intent, ContextHubInfo, ContextHubClientCallback,
+ * Executor)} with the executor using the main thread's Looper.
+ *
+ * @param intent the intent that is associated with a client
+ * @param hubInfo the hub to attach this client to
+ * @param callback the notification callback to register
+ * @return the registered client object
+ *
+ * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or the intent
+ * was not associated with a client
+ * @throws IllegalStateException if there were too many registered clients at the service
+ * @throws NullPointerException if intent, hubInfo, or callback is null
+ *
+ * @hide
+ */
+ @NonNull public ContextHubClient createClient(
+ @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo,
+ @NonNull ContextHubClientCallback callback) {
+ return createClient(intent, hubInfo, callback, new HandlerExecutor(Handler.getMain()));
+ }
+
+ /**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b948402..f7f627e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -410,14 +410,6 @@
@GuardedBy("mLock")
private boolean mNotifyUserActionSent;
- /**
- * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
- * have not shown our own window yet. In this situation, the previous inset continues to be
- * shown as an empty region until it is explicitly updated. Basically we can trigger the update
- * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
- */
- boolean mShouldClearInsetOfPreviousIme;
-
@UnsupportedAppUsage
final Insets mTmpInsets = new Insets();
final int[] mTmpLocation = new int[2];
@@ -581,7 +573,6 @@
mShowInputFlags = 0;
mShowInputRequested = false;
doHideWindow();
- clearInsetOfPreviousIme();
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_HIDDEN
@@ -601,7 +592,6 @@
if (dispatchOnShowInputRequested(flags, false)) {
showWindow(true);
}
- clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
@@ -946,9 +936,6 @@
super.onCreate();
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mSettingsObserver = SettingsObserver.createAndRegister(this);
- // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
- // we continue to use the same size of the inset or update it
- mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
// TODO(b/111364446) Need to address context lifecycle issue if need to re-create
// for update resources & configuration correctly when show soft input
// in non-default display.
@@ -1882,9 +1869,6 @@
if (DEBUG) Log.v(TAG, "showWindow: showing!");
onWindowShown();
mWindow.show();
- // Put here rather than in onWindowShown() in case people forget to call
- // super.onWindowShown().
- mShouldClearInsetOfPreviousIme = false;
}
}
@@ -1934,32 +1918,6 @@
}
/**
- * Reset the inset occupied the previous IME when and only when
- * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
- */
- private void clearInsetOfPreviousIme() {
- if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
- + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
- if (!mShouldClearInsetOfPreviousIme) return;
-
- clearLastInputMethodWindowForTransition();
- mShouldClearInsetOfPreviousIme = false;
- }
-
- /**
- * Tells the system that the IME decided to not show a window and the system no longer needs to
- * use the previous IME's inset.
- *
- * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()}
- * is the only expected caller of this method. Do not depend on this anywhere else.</p>
- *
- * <p>TODO: We probably need to reconsider how IME should be handled.</p>
- */
- private void clearLastInputMethodWindowForTransition() {
- mPrivOps.clearLastInputMethodWindowForTransition();
- }
-
- /**
* Called when a new client has bound to the input method. This
* may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
* and {@link #onFinishInput()} calls as the user navigates through its
@@ -2845,18 +2803,22 @@
}
/**
- * @return The recommended height of the input method window.
- * An IME author can get the last input method's height as the recommended height
- * by calling this in
- * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
- * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
- * switching by using this value as a visible inset height. It's efficient for the smooth
- * transition between different IMEs. However, note that this may return 0 (or possibly
- * unexpectedly low height). You should thus avoid relying on the return value of this method
- * all the time. Please make sure to use a reasonable height for the IME.
+ * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual
+ * semantics has never been well defined.
+ *
+ * <p>Note that the previous document clearly mentioned that this method could return {@code 0}
+ * at any time for whatever reason. Now this method is just always returning {@code 0}.</p>
+ *
+ * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method
+ * always returns {@code 0}
+ * @deprecated the actual behavior of this method has never been well defined. You cannot use
+ * this method in a reliable and predictable way
*/
+ @Deprecated
public int getInputMethodWindowRecommendedHeight() {
- return mImm.getInputMethodWindowVisibleHeight();
+ Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0."
+ + " Do not use this method.");
+ return 0;
}
/**
@@ -2980,7 +2942,6 @@
+ " visibleTopInsets=" + mTmpInsets.visibleTopInsets
+ " touchableInsets=" + mTmpInsets.touchableInsets
+ " touchableRegion=" + mTmpInsets.touchableRegion);
- p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
p.println(" mSettingsObserver=" + mSettingsObserver);
}
}
diff --git a/core/java/android/net/ConnectionInfo.aidl b/core/java/android/net/ConnectionInfo.aidl
new file mode 100644
index 0000000..07faf8b
--- /dev/null
+++ b/core/java/android/net/ConnectionInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright (C) 2018 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable ConnectionInfo;
diff --git a/core/java/android/net/ConnectionInfo.java b/core/java/android/net/ConnectionInfo.java
new file mode 100644
index 0000000..58d0e05
--- /dev/null
+++ b/core/java/android/net/ConnectionInfo.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 android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Describe a network connection including local and remote address/port of a connection and the
+ * transport protocol.
+ *
+ * @hide
+ */
+public final class ConnectionInfo implements Parcelable {
+ public final int protocol;
+ public final InetSocketAddress local;
+ public final InetSocketAddress remote;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public ConnectionInfo(int protocol, InetSocketAddress local, InetSocketAddress remote) {
+ this.protocol = protocol;
+ this.local = local;
+ this.remote = remote;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(protocol);
+ out.writeByteArray(local.getAddress().getAddress());
+ out.writeInt(local.getPort());
+ out.writeByteArray(remote.getAddress().getAddress());
+ out.writeInt(remote.getPort());
+ }
+
+ public static final Creator<ConnectionInfo> CREATOR = new Creator<ConnectionInfo>() {
+ public ConnectionInfo createFromParcel(Parcel in) {
+ int protocol = in.readInt();
+ InetAddress localAddress;
+ try {
+ localAddress = InetAddress.getByAddress(in.createByteArray());
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("Invalid InetAddress");
+ }
+ int localPort = in.readInt();
+ InetAddress remoteAddress;
+ try {
+ remoteAddress = InetAddress.getByAddress(in.createByteArray());
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("Invalid InetAddress");
+ }
+ int remotePort = in.readInt();
+ InetSocketAddress local = new InetSocketAddress(localAddress, localPort);
+ InetSocketAddress remote = new InetSocketAddress(remoteAddress, remotePort);
+ return new ConnectionInfo(protocol, local, remote);
+ }
+
+ public ConnectionInfo[] newArray(int size) {
+ return new ConnectionInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ce18796..8333b81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -26,7 +26,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -59,6 +58,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -3800,8 +3800,9 @@
private void unsupportedStartingFrom(int version) {
if (Process.myUid() == Process.SYSTEM_UID) {
- // The getApplicationInfo() call we make below is not supported in system context, and
- // we want to allow the system to use these APIs anyway.
+ // The getApplicationInfo() call we make below is not supported in system context. Let
+ // the call through here, and rely on the fact that ConnectivityService will refuse to
+ // allow the system to use these APIs anyway.
return;
}
@@ -3818,11 +3819,6 @@
// functions by accessing ConnectivityService directly. However, it should be clear that doing
// so is unsupported and may break in the future. http://b/22728205
private void checkLegacyRoutingApiAccess() {
- if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
unsupportedStartingFrom(VERSION_CODES.M);
}
@@ -3930,4 +3926,26 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the {@code uid} of the owner of a network connection.
+ *
+ * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and
+ * {@code IPPROTO_UDP} currently supported.
+ * @param local The local {@link InetSocketAddress} of a connection.
+ * @param remote The remote {@link InetSocketAddress} of a connection.
+ *
+ * @return {@code uid} if the connection is found and the app has permission to observe it
+ * (e.g., if it is associated with the calling VPN app's tunnel) or
+ * {@link android.os.Process#INVALID_UID} if the connection is not found.
+ */
+ public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote) {
+ ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
+ try {
+ return mService.getConnectionOwnerUid(connectionInfo);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index ce95b60..e7d441d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.app.PendingIntent;
+import android.net.ConnectionInfo;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -182,4 +183,6 @@
String getCaptivePortalServerUrl();
byte[] getNetworkWatchlistConfigHash();
+
+ int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b9d9007..f947b5e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -773,6 +773,7 @@
if (tracingEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
}
+ ThreadLocalWorkSourceUid.set(Binder.getCallingUid());
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
if (observer != null) {
@@ -793,6 +794,7 @@
}
res = true;
} finally {
+ ThreadLocalWorkSourceUid.clear();
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6bd2e76..8681893 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -30,6 +30,8 @@
import dalvik.system.VMRuntime;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -1083,7 +1085,67 @@
return true;
}
+ /** Build information for a particular device partition. */
+ public static class Partition {
+ /** The name identifying the system partition. */
+ public static final String PARTITION_NAME_SYSTEM = "system";
+
+ private String mName;
+ private String mFingerprint;
+ private long mTimeMs;
+
+ public Partition() {}
+
+ private Partition(String name, String fingerprint, long timeMs) {
+ mName = name;
+ mFingerprint = fingerprint;
+ mTimeMs = timeMs;
+ }
+
+ /** The name of this partition, e.g. "system", or "vendor" */
+ public String getName() {
+ return mName;
+ }
+
+ /** The build fingerprint of this partition, see {@link Build#FINGERPRINT}. */
+ public String getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** The time (ms since epoch), at which this partition was built, see {@link Build#TIME}. */
+ public long getTimeMillis() {
+ return mTimeMs;
+ }
+ }
+
+ /**
+ * Get build information about partitions that have a separate fingerprint defined.
+ *
+ * The list includes partitions that are suitable candidates for over-the-air updates. This is
+ * not an exhaustive list of partitions on the device.
+ */
+ public static List<Partition> getPartitions() {
+ ArrayList<Partition> partitions = new ArrayList();
+
+ String[] names = new String[] {
+ "bootimage", "odm", "product", "product_services", Partition.PARTITION_NAME_SYSTEM,
+ "vendor"
+ };
+ for (String name : names) {
+ String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint");
+ if (TextUtils.isEmpty(fingerprint)) {
+ continue;
+ }
+ long time = getLong("ro." + name + ".build.date.utc") * 1000;
+ partitions.add(new Partition(name, fingerprint, time));
+ }
+
+ return partitions;
+ }
+
// The following properties only make sense for internal engineering builds.
+
+ /** The time at which the build was produced, given in milliseconds since the UNIX epoch. */
public static final long TIME = getLong("ro.build.date.utc") * 1000;
public static final String USER = getString("ro.build.user");
public static final String HOST = getString("ro.build.host");
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index f2e0bdd..54be639 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -44,6 +44,8 @@
private static final boolean DEBUG = false;
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+ private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+ private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -54,6 +56,7 @@
*/
public void setup(Context context) {
setupGpuLayers(context);
+ setupAngle(context);
chooseDriver(context);
}
@@ -121,7 +124,6 @@
}
}
}
-
}
// Include the app's lib directory in all cases
@@ -131,6 +133,80 @@
}
/**
+ * Pass ANGLE details down to trigger enable logic
+ */
+ private static void setupAngle(Context context) {
+
+ String angleEnabledApp =
+ Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.ANGLE_ENABLED_APP);
+
+ String packageName = context.getPackageName();
+
+ boolean devOptIn = false;
+ if ((angleEnabledApp != null && packageName != null)
+ && (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
+ && angleEnabledApp.equals(packageName)) {
+
+ if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting");
+
+ devOptIn = true;
+ }
+
+ ApplicationInfo appInfo;
+ try {
+ appInfo = context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to get info about current application: " + packageName);
+ return;
+ }
+
+ String appPref = "dontcare";
+ final BaseBundle metadata = appInfo.metaData;
+ if (metadata != null) {
+ final String glesMode = metadata.getString(GLES_MODE_METADATA_KEY);
+ if (glesMode != null) {
+ if (glesMode.equals("angle")) {
+ appPref = "angle";
+ if (DEBUG) Log.v(TAG, packageName + " opted for ANGLE via AndroidManifest");
+ } else if (glesMode.equals("native")) {
+ appPref = "native";
+ if (DEBUG) Log.v(TAG, packageName + " opted for NATIVE via AndroidManifest");
+ } else {
+ Log.w(TAG, "Unrecognized GLES_MODE (\"" + glesMode + "\") for " + packageName
+ + ". Supported values are \"angle\" or \"native\"");
+ }
+ }
+ }
+
+ ApplicationInfo angleInfo;
+ try {
+ angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+ return;
+ }
+
+ String abi = chooseAbi(angleInfo);
+
+ // Build a path that includes installed native libs and APK
+ StringBuilder sb = new StringBuilder();
+ sb.append(angleInfo.nativeLibraryDir)
+ .append(File.pathSeparator)
+ .append(angleInfo.sourceDir)
+ .append("!/lib/")
+ .append(abi);
+ String paths = sb.toString();
+
+ if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
+
+ // Further opt-in logic is handled in native, so pass relevant info down
+ setAngleInfo(paths, packageName, appPref, devOptIn);
+ }
+
+ /**
* Choose whether the current process should use the builtin or an updated driver.
*/
private static void chooseDriver(Context context) {
@@ -218,4 +294,6 @@
private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
private static native void setDebugLayers(String layers);
private static native void setDriverPath(String path);
+ private static native void setAngleInfo(String path, String appPackage, String appPref,
+ boolean devOptIn);
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index e03af9d..f3a9a50 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -739,6 +739,8 @@
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
+ msg.workSourceUid = ThreadLocalWorkSourceUid.get();
+
if (mAsynchronous) {
msg.setAsynchronous(true);
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index ded3a19..5b8abab 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -205,6 +205,7 @@
token = observer.messageDispatchStarting();
}
try {
+ ThreadLocalWorkSourceUid.set(msg.workSourceUid);
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
@@ -216,6 +217,7 @@
}
throw exception;
} finally {
+ ThreadLocalWorkSourceUid.clear();
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 455d8ed..cd3f301 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,7 +16,6 @@
package android.os;
-import android.os.MessageProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -74,11 +73,25 @@
public Messenger replyTo;
/**
+ * Indicates that the uid is not set;
+ *
+ * @hide Only for use within the system server.
+ */
+ public static final int UID_NONE = -1;
+
+ /**
* Optional field indicating the uid that sent the message. This is
* only valid for messages posted by a {@link Messenger}; otherwise,
* it will be -1.
*/
- public int sendingUid = -1;
+ public int sendingUid = UID_NONE;
+
+ /**
+ * Optional field indicating the uid that caused this message to be enqueued.
+ *
+ * @hide Only for use within the system server.
+ */
+ public int workSourceUid = UID_NONE;
/** If set message is in use.
* This flag is set when the message is enqueued and remains set while it
@@ -151,6 +164,7 @@
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
+ m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
@@ -301,7 +315,8 @@
arg2 = 0;
obj = null;
replyTo = null;
- sendingUid = -1;
+ sendingUid = UID_NONE;
+ workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
@@ -329,6 +344,7 @@
this.obj = o.obj;
this.replyTo = o.replyTo;
this.sendingUid = o.sendingUid;
+ this.workSourceUid = o.workSourceUid;
if (o.data != null) {
this.data = (Bundle) o.data.clone();
@@ -612,6 +628,7 @@
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
+ dest.writeInt(workSourceUid);
}
private void readFromParcel(Parcel source) {
@@ -625,5 +642,6 @@
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
+ workSourceUid = source.readInt();
}
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 89a5def..8ea061e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1154,10 +1154,15 @@
*
* @return True if the set was allowed.
*
- * @see #isPowerSaveMode()
- *
* @hide
+ * @see #isPowerSaveMode()
*/
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.DEVICE_POWER,
+ android.Manifest.permission.POWER_SAVER
+ })
public boolean setPowerSaveMode(boolean mode) {
try {
return mService.setPowerSaveMode(mode);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 5cd2ffc..0f64c45 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -42,6 +42,11 @@
public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
/**
+ * An invalid UID value.
+ */
+ public static final int INVALID_UID = -1;
+
+ /**
* Defines the root UID.
* @hide
*/
@@ -478,6 +483,8 @@
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param packagesForUid null-ok all the packages with the same uid as this process.
+ * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -496,10 +503,13 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, packageName,
+ packagesForUid, visibleVols, zygoteArgs);
}
/** @hide */
@@ -514,10 +524,13 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, packageName,
+ packagesForUid, visibleVols, zygoteArgs);
}
/**
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 3e8e885..8492b0c 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -36,16 +36,12 @@
import android.view.Display;
import android.view.WindowManager;
-import com.android.internal.logging.MetricsLogger;
-
import libcore.io.Streams;
-import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -97,7 +93,7 @@
/** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */
private static final File RECOVERY_DIR = new File("/cache/recovery");
private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
- private static final File LAST_INSTALL_FILE = new File(RECOVERY_DIR, "last_install");
+ private static final String LAST_INSTALL_PATH = "last_install";
private static final String LAST_PREFIX = "last_";
private static final String ACTION_EUICC_FACTORY_RESET =
"com.android.internal.action.EUICC_FACTORY_RESET";
@@ -935,116 +931,6 @@
throw new IOException("Reboot failed (no permissions?)");
}
- // Read last_install; then report time (in seconds) and I/O (in MiB) for
- // this update to tron.
- // Only report on the reboots immediately after an OTA update.
- private static void parseLastInstallLog(Context context) {
- try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {
- String line = null;
- int bytesWrittenInMiB = -1, bytesStashedInMiB = -1;
- int timeTotal = -1;
- int uncryptTime = -1;
- int sourceVersion = -1;
- int temperatureStart = -1;
- int temperatureEnd = -1;
- int temperatureMax = -1;
- int errorCode = -1;
- int causeCode = -1;
-
- while ((line = in.readLine()) != null) {
- // Here is an example of lines in last_install:
- // ...
- // time_total: 101
- // bytes_written_vendor: 51074
- // bytes_stashed_vendor: 200
- int numIndex = line.indexOf(':');
- if (numIndex == -1 || numIndex + 1 >= line.length()) {
- continue;
- }
- String numString = line.substring(numIndex + 1).trim();
- long parsedNum;
- try {
- parsedNum = Long.parseLong(numString);
- } catch (NumberFormatException ignored) {
- Log.e(TAG, "Failed to parse numbers in " + line);
- continue;
- }
-
- final int MiB = 1024 * 1024;
- int scaled;
- try {
- if (line.startsWith("bytes")) {
- scaled = Math.toIntExact(parsedNum / MiB);
- } else {
- scaled = Math.toIntExact(parsedNum);
- }
- } catch (ArithmeticException ignored) {
- Log.e(TAG, "Number overflows in " + line);
- continue;
- }
-
- if (line.startsWith("time")) {
- timeTotal = scaled;
- } else if (line.startsWith("uncrypt_time")) {
- uncryptTime = scaled;
- } else if (line.startsWith("source_build")) {
- sourceVersion = scaled;
- } else if (line.startsWith("bytes_written")) {
- bytesWrittenInMiB = (bytesWrittenInMiB == -1) ? scaled :
- bytesWrittenInMiB + scaled;
- } else if (line.startsWith("bytes_stashed")) {
- bytesStashedInMiB = (bytesStashedInMiB == -1) ? scaled :
- bytesStashedInMiB + scaled;
- } else if (line.startsWith("temperature_start")) {
- temperatureStart = scaled;
- } else if (line.startsWith("temperature_end")) {
- temperatureEnd = scaled;
- } else if (line.startsWith("temperature_max")) {
- temperatureMax = scaled;
- } else if (line.startsWith("error")) {
- errorCode = scaled;
- } else if (line.startsWith("cause")) {
- causeCode = scaled;
- }
- }
-
- // Don't report data to tron if corresponding entry isn't found in last_install.
- if (timeTotal != -1) {
- MetricsLogger.histogram(context, "ota_time_total", timeTotal);
- }
- if (uncryptTime != -1) {
- MetricsLogger.histogram(context, "ota_uncrypt_time", uncryptTime);
- }
- if (sourceVersion != -1) {
- MetricsLogger.histogram(context, "ota_source_version", sourceVersion);
- }
- if (bytesWrittenInMiB != -1) {
- MetricsLogger.histogram(context, "ota_written_in_MiBs", bytesWrittenInMiB);
- }
- if (bytesStashedInMiB != -1) {
- MetricsLogger.histogram(context, "ota_stashed_in_MiBs", bytesStashedInMiB);
- }
- if (temperatureStart != -1) {
- MetricsLogger.histogram(context, "ota_temperature_start", temperatureStart);
- }
- if (temperatureEnd != -1) {
- MetricsLogger.histogram(context, "ota_temperature_end", temperatureEnd);
- }
- if (temperatureMax != -1) {
- MetricsLogger.histogram(context, "ota_temperature_max", temperatureMax);
- }
- if (errorCode != -1) {
- MetricsLogger.histogram(context, "ota_non_ab_error_code", errorCode);
- }
- if (causeCode != -1) {
- MetricsLogger.histogram(context, "ota_non_ab_cause_code", causeCode);
- }
-
- } catch (IOException e) {
- Log.e(TAG, "Failed to read lines in last_install", e);
- }
- }
-
/**
* Called after booting to process and remove recovery-related files.
* @return the log file from recovery, or null if none was found.
@@ -1062,9 +948,6 @@
Log.e(TAG, "Error reading recovery log", e);
}
- if (log != null) {
- parseLastInstallLog(context);
- }
// Only remove the OTA package if it's partially processed (uncrypt'd).
boolean reservePackage = BLOCK_MAP_FILE.exists();
@@ -1095,7 +978,8 @@
// GmsCore to avoid re-downloading everything again.
String[] names = RECOVERY_DIR.list();
for (int i = 0; names != null && i < names.length; i++) {
- if (names[i].startsWith(LAST_PREFIX)) continue;
+ // Do not remove the last_install file since the recovery-persist takes care of it.
+ if (names[i].startsWith(LAST_PREFIX) || names[i].equals(LAST_INSTALL_PATH)) continue;
if (reservePackage && names[i].equals(BLOCK_MAP_FILE.getName())) continue;
if (reservePackage && names[i].equals(UNCRYPT_PACKAGE_FILE.getName())) continue;
diff --git a/core/java/android/os/ThreadLocalWorkSourceUid.java b/core/java/android/os/ThreadLocalWorkSourceUid.java
new file mode 100644
index 0000000..df1d275
--- /dev/null
+++ b/core/java/android/os/ThreadLocalWorkSourceUid.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 android.os;
+
+/**
+ * @hide Only for use within system server.
+ */
+public final class ThreadLocalWorkSourceUid {
+ public static final int UID_NONE = Message.UID_NONE;
+ private static final ThreadLocal<Integer> sWorkSourceUid =
+ ThreadLocal.withInitial(() -> UID_NONE);
+
+ /** Returns the original work source uid. */
+ public static int get() {
+ return sWorkSourceUid.get();
+ }
+
+ /** Sets the original work source uid. */
+ public static void set(int uid) {
+ sWorkSourceUid.set(uid);
+ }
+
+ /** Clears the stored work source uid. */
+ public static void clear() {
+ sWorkSourceUid.set(UID_NONE);
+ }
+
+ private ThreadLocalWorkSourceUid() {
+ }
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b0891050..1282170 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -256,6 +256,7 @@
/**
* Specifies if a user is disallowed from enabling the
* "Unknown Sources" setting, that allows installation of apps from unknown sources.
+ * Unknown sources exclude adb and special apps such as trusted app stores.
* The default value is <code>false</code>.
*
* <p>Key for user restrictions.
@@ -267,6 +268,22 @@
public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
/**
+ * This restriction is a device-wide version of {@link DISALLOW_INSTALL_UNKNOWN_SOURCES}.
+ *
+ * Specifies if all users on the device are disallowed from enabling the
+ * "Unknown Sources" setting, that allows installation of apps from unknown sources.
+ * The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY =
+ "no_install_unknown_sources_globally";
+
+ /**
* Specifies if a user is disallowed from configuring bluetooth.
* This does <em>not</em> restrict the user from turning bluetooth on or off.
* The default value is <code>false</code>.
@@ -1669,8 +1686,9 @@
/**
* @hide
* Returns whether the given user has been disallowed from performing certain actions
- * or setting certain settings through UserManager. This method disregards restrictions
- * set by device policy.
+ * or setting certain settings through UserManager (e.g. this type of restriction would prevent
+ * the guest user from doing certain things, such as making calls). This method disregards
+ * restrictions set by device policy.
* @param restrictionKey the string key representing the restriction
* @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
*/
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 99181ac..7fd0a4b 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -215,6 +215,8 @@
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param packagesForUid null-ok all the packages with the same uid as this process.
+ * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -231,12 +233,14 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
- packageName, zygoteArgs);
+ packageName, packagesForUid, visibleVols, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -355,6 +359,8 @@
* @param startChildZygote Start a sub-zygote. This creates a new zygote process
* that has its state cloned from this zygote process.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param packagesForUid null-ok all the packages with the same uid as this process.
+ * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -372,6 +378,8 @@
@Nullable String invokeWith,
boolean startChildZygote,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -439,6 +447,32 @@
argsForZygote.add("--package-name=" + packageName);
}
+ if (packagesForUid != null && packagesForUid.length > 0) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("--packages-for-uid=");
+
+ for (int i = 0; i < packagesForUid.length; ++i) {
+ if (i != 0) {
+ sb.append(',');
+ }
+ sb.append(packagesForUid[i]);
+ }
+ argsForZygote.add(sb.toString());
+ }
+
+ if (visibleVols != null && visibleVols.length > 0) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("--visible-vols=");
+
+ for (int i = 0; i < visibleVols.length; ++i) {
+ if (i != 0) {
+ sb.append(',');
+ }
+ sb.append(visibleVols[i]);
+ }
+ argsForZygote.add(sb.toString());
+ }
+
argsForZygote.add(processClass);
if (extraArgs != null) {
@@ -746,7 +780,8 @@
result = startViaZygote(processClass, niceName, uid, gid,
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
- true /* startChildZygote */, null /* packageName */, extraArgs);
+ true /* startChildZygote */, null /* packageName */,
+ null /* packagesForUid */, null /* visibleVolumes */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index d850e27..1f54ea5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -89,8 +89,13 @@
* @param appId The appId for the given package.
* @param sharedUserId The sharedUserId for given package if it specified
* {@code android:sharedUserId} in the manifest, otherwise {@code null}
- * @param userId
+ * @param userId The userId in which the storage needs to be mounted.
*/
public abstract void mountExternalStorageForApp(String packageName, int appId,
String sharedUserId, int userId);
+
+ /**
+ * @return Labels of storage volumes that are visible to the given userId.
+ */
+ public abstract String[] getVisibleVolumesForUser(int userId);
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index afd38369..e55afb6 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -157,7 +157,7 @@
public final DiskInfo disk;
public final String partGuid;
public int mountFlags = 0;
- public int mountUserId = -1;
+ public int mountUserId = UserHandle.USER_NULL;
@UnsupportedAppUsage
public int state = STATE_UNMOUNTED;
public String fsType;
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
new file mode 100644
index 0000000..aa44eb7
--- /dev/null
+++ b/core/java/android/permission/PermissionManager.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+import com.android.internal.annotations.Immutable;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * System level service for accessing the permission capabilities of the platform.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.PERMISSION_SERVICE)
+public final class PermissionManager {
+ /**
+ * {@link android.content.pm.PackageParser} needs access without having a {@link Context}.
+ *
+ * @hide
+ */
+ public static final SplitPermissionInfo[] SPLIT_PERMISSIONS = new SplitPermissionInfo[]{
+ // READ_EXTERNAL_STORAGE is always required when an app requests
+ // WRITE_EXTERNAL_STORAGE, because we can't have an app that has
+ // write access without read access. The hack here with the target
+ // target SDK version ensures that this grant is always done.
+ new SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
+ android.os.Build.VERSION_CODES.CUR_DEVELOPMENT + 1),
+ new SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS,
+ new String[]{android.Manifest.permission.READ_CALL_LOG},
+ android.os.Build.VERSION_CODES.JELLY_BEAN),
+ new SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS,
+ new String[]{android.Manifest.permission.WRITE_CALL_LOG},
+ android.os.Build.VERSION_CODES.JELLY_BEAN),
+ new SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION,
+ new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ android.os.Build.VERSION_CODES.P0),
+ new SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION,
+ new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ android.os.Build.VERSION_CODES.P0)};
+
+ private final @NonNull Context mContext;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context in which to operate.
+ * @hide
+ */
+ public PermissionManager(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Get list of permissions that have been split into more granular or dependent permissions.
+ *
+ * <p>E.g. before {@link android.os.Build.VERSION_CODES#P0} an app that was granted
+ * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in
+ * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#P0}
+ * the location permission only grants location access while the app is in foreground. This
+ * would break apps that target before {@link android.os.Build.VERSION_CODES#P0}. Hence whenever
+ * such an old app asks for a location permission (i.e. the
+ * {@link SplitPermissionInfo#getRootPermission()}), then the
+ * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside
+ * {@{@link SplitPermissionInfo#getNewPermissions}) is added.
+ *
+ * <p>Note: Regular apps do not have to worry about this. The platform and permission controller
+ * automatically add the new permissions where needed.
+ *
+ * @return All permissions that are split.
+ */
+ public @NonNull List<SplitPermissionInfo> getSplitPermissions() {
+ return Arrays.asList(SPLIT_PERMISSIONS);
+ }
+
+ /**
+ * A permission that was added in a previous API level might have split into several
+ * permissions. This object describes one such split.
+ */
+ @Immutable
+ public static final class SplitPermissionInfo {
+ private final @NonNull String mRootPerm;
+ private final @NonNull String[] mNewPerms;
+ private final int mTargetSdk;
+
+ /**
+ * Get the permission that is split.
+ */
+ public @NonNull String getRootPermission() {
+ return mRootPerm;
+ }
+
+ /**
+ * Get the permissions that are added.
+ */
+ public @NonNull String[] getNewPermissions() {
+ return mNewPerms;
+ }
+
+ /**
+ * Get the target API level when the permission was split.
+ */
+ public int getTargetSdk() {
+ return mTargetSdk;
+ }
+
+ private SplitPermissionInfo(@NonNull String rootPerm, @NonNull String[] newPerms,
+ int targetSdk) {
+ mRootPerm = rootPerm;
+ mNewPerms = newPerms;
+ mTargetSdk = targetSdk;
+ }
+ }
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ee64ca2..8c40e0e 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -942,13 +942,26 @@
return false;
}
- /** {@hide} */
+ /**
+ * 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 */);
+ }
+
+ /**
+ * 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) {
- if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
- final List<String> paths = uri.getPathSegments();
- return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0)));
- }
- return false;
+ return isRootUri(context, uri, 2 /* pathSize */);
}
/** {@hide} */
@@ -967,6 +980,14 @@
return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
}
+ private static boolean isRootUri(Context context, @Nullable Uri uri, int pathSize) {
+ if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+ final List<String> paths = uri.getPathSegments();
+ return (paths.size() == pathSize && PATH_ROOT.equals(paths.get(0)));
+ }
+ return false;
+ }
+
private static boolean isDocumentsProvider(Context context, String authority) {
final Intent intent = new Intent(PROVIDER_INTERFACE);
final List<ResolveInfo> infos = context.getPackageManager()
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 82459b1..828fd73 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1318,18 +1318,6 @@
}
public static final class Media implements AudioColumns {
-
- private static final String[] EXTERNAL_PATHS;
-
- static {
- String secondary_storage = System.getenv("SECONDARY_STORAGE");
- if (secondary_storage != null) {
- EXTERNAL_PATHS = secondary_storage.split(":");
- } else {
- EXTERNAL_PATHS = new String[0];
- }
- }
-
/**
* Get the content:// style URI for the audio media table on the
* given volume.
@@ -1343,14 +1331,9 @@
}
public static Uri getContentUriForPath(String path) {
- for (String ep : EXTERNAL_PATHS) {
- if (path.startsWith(ep)) {
- return EXTERNAL_CONTENT_URI;
- }
- }
-
- return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
- EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
+ return (path.startsWith(
+ Environment.getStorageDirectory().getAbsolutePath() + "/")
+ ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index aa178fb..e1c0fe5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7267,12 +7267,12 @@
private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * Whether the device should pulse on reach gesture.
+ * Gesture that wakes up the lock screen.
* @hide
*/
- public static final String DOZE_REACH_GESTURE = "doze_reach_gesture";
+ public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_lock_screen_gesture";
- private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+ private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
* Gesture that wakes up the display, showing the ambient version of the status bar.
@@ -7689,6 +7689,15 @@
BOOLEAN_VALIDATOR;
/**
+ * Whether or not face unlock is allowed for apps (through BiometricPrompt).
+ * @hide
+ */
+ public static final String FACE_UNLOCK_APP_ENABLED = "face_unlock_app_enabled";
+
+ private static final Validator FACE_UNLOCK_APP_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
+ /**
* Whether the assist gesture should be enabled.
*
* @hide
@@ -8184,11 +8193,12 @@
DOZE_ALWAYS_ON,
DOZE_PICK_UP_GESTURE,
DOZE_DOUBLE_TAP_GESTURE,
- DOZE_REACH_GESTURE,
+ DOZE_WAKE_LOCK_SCREEN_GESTURE,
DOZE_WAKE_SCREEN_GESTURE,
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
FACE_UNLOCK_KEYGUARD_ENABLED,
+ FACE_UNLOCK_APP_ENABLED,
ASSIST_GESTURE_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_WAKE_ENABLED,
@@ -8331,12 +8341,13 @@
VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR);
VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
- VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR);
+ VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR);
+ VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -9593,8 +9604,7 @@
* Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
*
* Integer values are interpreted as boolean, and the absence of an explicit setting
- * is interpreted as |true|.
- * TODO: make the default |false|
+ * is interpreted as |false|.
* @hide
*/
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
@@ -10811,6 +10821,12 @@
= "activity_starts_logging_enabled";
/**
+ * @hide
+ * @see com.android.server.appbinding.AppBindingConstants
+ */
+ public static final String APP_BINDING_CONSTANTS = "app_binding_constants";
+
+ /**
* App ops specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -11570,6 +11586,12 @@
public static final String GPU_DEBUG_APP = "gpu_debug_app";
/**
+ * App should try to use ANGLE
+ * @hide
+ */
+ public static final String ANGLE_ENABLED_APP = "angle_enabled_app";
+
+ /**
* Ordered GPU debug layer list
* i.e. <layer1>:<layer2>:...:<layerN>
* @hide
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index 119f5d0..4f4d62a 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -16,26 +16,23 @@
package android.security.net.config;
-import android.os.Environment;
-import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
+
+import libcore.io.IoUtils;
+
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.InputStream;
import java.io.IOException;
-import java.security.cert.Certificate;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Set;
-import libcore.io.IoUtils;
-
-import com.android.org.conscrypt.Hex;
-import com.android.org.conscrypt.NativeCrypto;
import javax.security.auth.x500.X500Principal;
@@ -192,8 +189,36 @@
}
private String getHash(X500Principal name) {
- int hash = NativeCrypto.X509_NAME_hash_old(name);
- return Hex.intToHexString(hash, 8);
+ int hash = hashName(name);
+ return intToHexString(hash, 8);
+ }
+
+ private static final char[] DIGITS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ private static String intToHexString(int i, int minWidth) {
+ int bufLen = 8; // Max number of hex digits in an int
+ char[] buf = new char[bufLen];
+ int cursor = bufLen;
+
+ do {
+ buf[--cursor] = DIGITS[i & 0xf];
+ } while ((i >>>= 4) != 0 || (bufLen - cursor < minWidth));
+
+ return new String(buf, cursor, bufLen - cursor);
+ }
+
+ // This code matches the code in X509_NAME_hash_old() in Conscrypt, which in turn matches
+ // the names of certificate files. It must be kept in sync.
+ private static int hashName(X500Principal principal) {
+ try {
+ byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded());
+ int offset = 0;
+ return (((digest[offset++] & 0xff) << 0) | ((digest[offset++] & 0xff) << 8)
+ | ((digest[offset++] & 0xff) << 16) | ((digest[offset] & 0xff) << 24));
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
}
private X509Certificate readCertificate(String file) {
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 6c18b45..573d577 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -651,14 +651,11 @@
/**
* Called when the user requests the service to save the contents of a screen.
*
- * <p>Service must call one of the {@link SaveCallback} methods (like
- * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
- * to notify the Android System of the result of the request.
- *
* <p>If the service could not handle the request right away—for example, because it must
* launch an activity asking the user to authenticate first or because the network is
* down—the service could keep the {@link SaveRequest request} and reuse it later,
- * but the service must call {@link SaveCallback#onSuccess()} right away.
+ * but the service <b>must always</b> call {@link SaveCallback#onSuccess()} or
+ * {@link SaveCallback#onSuccess(android.content.IntentSender)} right away.
*
* <p><b>Note:</b> To retrieve the actual value of fields input by the user, the service
* should call
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 663bdcb..06e2896 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -43,13 +43,20 @@
}
/**
- * Notifies the Android System that an
- * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
- * FillCallback)} was successfully fulfilled by the service.
+ * Notifies the Android System that a fill request
+ * ({@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)}) was successfully fulfilled by the service.
*
- * @param response autofill information for that activity, or {@code null} when the activity
- * cannot be autofilled (for example, if it only contains read-only fields). See
- * {@link FillResponse} for examples.
+ * <p>This method should always be called, even if the service doesn't have the heuristics to
+ * fulfill the request (in which case it should be called with {@code null}).
+ *
+ * <p>See the main {@link AutofillService} documentation for more details and examples.
+ *
+ * @param response autofill information for that activity, or {@code null} when the service
+ * cannot autofill the activity.
+ *
+ * @throws IllegalStateException if this method or {@link #onFailure(CharSequence)} was already
+ * called.
*/
public void onSuccess(@Nullable FillResponse response) {
assertNotCalled();
@@ -67,14 +74,33 @@
}
/**
- * Notifies the Android System that an
+ * Notifies the Android System that a fill request (
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
- * FillCallback)} could not be fulfilled by the service.
+ * FillCallback)}) could not be fulfilled by the service (for example, because the user data was
+ * not available yet), so the request could be retried later.
*
- * @param message error message to be displayed to the user.
+ * <p><b>Note: </b>this method should not be used when the service didn't have the heursitics to
+ * fulfill the request; in this case, the service should call {@link #onSuccess(FillResponse)
+ * onSuccess(null)} instead.
+ *
+ * <p><b>Note: </b>prior to {@link android.os.Build.VERSION_CODES#Q}, this
+ * method was not working as intended and the service should always call
+ * {@link #onSuccess(FillResponse) onSuccess(null)} instead.
+ *
+ * <p><b>Note: </b>for apps targeting {@link android.os.Build.VERSION_CODES#Q} or higher, this
+ * method just logs the message on {@code logcat}; for apps targetting older SDKs, it also
+ * displays the message to user using a {@link android.widget.Toast}. Generally speaking, you
+ * should not display an error to the user if the request failed, unless the request had the
+ * {@link FillRequest#FLAG_MANUAL_REQUEST} flag.
+ *
+ * @param message error message. <b>Note: </b> this message should <b>not</b> contain PII
+ * (Personally Identifiable Information, such as username or email address).
+ *
+ * @throws IllegalStateException if this method or {@link #onSuccess(FillResponse)} was already
+ * called.
*/
public void onFailure(@Nullable CharSequence message) {
- Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
+ Log.w(TAG, "onFailure(): " + message);
assertNotCalled();
mCalled = true;
try {
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index e252e96..1753ecf 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -45,6 +45,9 @@
* Notifies the Android System that an
* {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled
* by the service.
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess(IntentSender)}, or
+ * {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess() {
onSuccessInternal(null);
@@ -62,6 +65,9 @@
*
* @param intentSender intent that will be launched from the context of activity being
* autofilled.
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess()},
+ * or {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess(@NonNull IntentSender intentSender) {
onSuccessInternal(Preconditions.checkNotNull(intentSender));
@@ -77,23 +83,30 @@
}
}
+
+
+
/**
* Notifies the Android System that an
* {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be handled
* by the service.
*
- * <p>This method should only be called when the service could not handle the request right away
- * and could not recover or retry it. If the service could retry or recover, it could keep
- * the {@link SaveRequest} and call {@link #onSuccess()} instead.
+ * <p>This method is just used for logging purposes, the Android System won't call the service
+ * again in case of failures—if you need to recover from the failure, just save the
+ * {@link SaveRequest} and try again later.
*
- * <p><b>Note:</b> The Android System displays an UI with the supplied error message; if
- * you prefer to show your own message, call {@link #onSuccess()} or
- * {@link #onSuccess(IntentSender)} instead.
+ * <p><b>Note: </b>for apps targeting {@link android.os.Build.VERSION_CODES#Q} or higher, this
+ * method just logs the message on {@code logcat}; for apps targetting older SDKs, it also
+ * displays the message to user using a {@link android.widget.Toast}.
*
- * @param message error message to be displayed to the user.
+ * @param message error message. <b>Note: </b> this message should <b>not</b> contain PII
+ * (Personally Identifiable Information, such as username or email address).
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess()},
+ * or {@link #onSuccess(IntentSender)} was already called.
*/
public void onFailure(CharSequence message) {
- Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
+ Log.w(TAG, "onFailure(): " + message);
assertNotCalled();
mCalled = true;
try {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e819c96..5012d77 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -146,6 +146,7 @@
private static final String RULE_ATT_CONDITION_ID = "conditionId";
private static final String RULE_ATT_CREATION_TIME = "creationTime";
private static final String RULE_ATT_ENABLER = "enabler";
+ private static final String RULE_ATT_MODIFIED = "modified";
@UnsupportedAppUsage
public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
@@ -633,6 +634,7 @@
Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
+ rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
return rt;
}
@@ -656,6 +658,7 @@
if (rule.condition != null) {
writeConditionXml(rule.condition, out);
}
+ out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
}
public static Condition readConditionXml(XmlPullParser parser) {
@@ -1456,6 +1459,7 @@
public long creationTime; // required for automatic
public String enabler; // package name, only used for manual rules.
public ZenPolicy zenPolicy;
+ public boolean modified; // rule has been modified from initial creation
public ZenRule() { }
@@ -1477,6 +1481,7 @@
enabler = source.readString();
}
zenPolicy = source.readParcelable(null);
+ modified = source.readInt() == 1;
}
@Override
@@ -1512,6 +1517,7 @@
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
+ dest.writeInt(modified ? 1 : 0);
}
@Override
@@ -1528,6 +1534,7 @@
.append(",creationTime=").append(creationTime)
.append(",enabler=").append(enabler)
.append(",zenPolicy=").append(zenPolicy)
+ .append(",modified=").append(modified)
.append(']').toString();
}
@@ -1554,6 +1561,7 @@
if (zenPolicy != null) {
zenPolicy.writeToProto(proto, ZenRuleProto.ZEN_POLICY);
}
+ proto.write(ZenRuleProto.MODIFIED, modified);
proto.end(token);
}
@@ -1606,6 +1614,9 @@
if (!Objects.equals(zenPolicy, to.zenPolicy)) {
d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy);
}
+ if (modified != to.modified) {
+ d.addLine(item, "modified", modified, to.modified);
+ }
}
@Override
@@ -1622,13 +1633,14 @@
&& Objects.equals(other.component, component)
&& Objects.equals(other.id, id)
&& Objects.equals(other.enabler, enabler)
- && Objects.equals(other.zenPolicy, zenPolicy);
+ && Objects.equals(other.zenPolicy, zenPolicy)
+ && other.modified == modified;
}
@Override
public int hashCode() {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, id, enabler, zenPolicy);
+ component, id, enabler, zenPolicy, modified);
}
public boolean isAutomaticActive() {
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e3d68a6..24819a6 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -16,6 +16,8 @@
package android.service.voice;
+import com.android.internal.app.IVoiceActionCheckCallback;
+
/**
* @hide
*/
@@ -24,4 +26,6 @@
void soundModelsChanged();
void shutdown();
void launchVoiceAssistFromKeyguard();
+ void getActiveServiceSupportedActions(in List<String> voiceActions,
+ in IVoiceActionCheckCallback callback);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0bbc07e..e105fdf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,6 +16,8 @@
package android.service.voice;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
@@ -26,17 +28,22 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* Top-level service of the current global voice interactor, which is providing
@@ -71,23 +78,43 @@
public static final String SERVICE_META_DATA = "android.voice_interaction";
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
- @Override public void ready() {
- mHandler.sendEmptyMessage(MSG_READY);
- }
- @Override public void shutdown() {
- mHandler.sendEmptyMessage(MSG_SHUTDOWN);
- }
- @Override public void soundModelsChanged() {
- mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
- }
@Override
- public void launchVoiceAssistFromKeyguard() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD);
+ public void ready() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onReady, VoiceInteractionService.this));
+ }
+
+ @Override
+ public void shutdown() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+ }
+
+ @Override
+ public void soundModelsChanged() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onSoundModelsChangedInternal,
+ VoiceInteractionService.this));
+ }
+
+ @Override
+ public void launchVoiceAssistFromKeyguard() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onLaunchVoiceAssistFromKeyguard,
+ VoiceInteractionService.this));
+ }
+
+ @Override
+ public void getActiveServiceSupportedActions(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ Handler.getMain().executeOrSendMessage(
+ PooledLambda.obtainMessage(VoiceInteractionService::onHandleVoiceActionCheck,
+ VoiceInteractionService.this,
+ voiceActions,
+ callback));
}
};
- MyHandler mHandler;
-
IVoiceInteractionManagerService mSystemService;
private final Object mLock = new Object();
@@ -96,33 +123,6 @@
private AlwaysOnHotwordDetector mHotwordDetector;
- static final int MSG_READY = 1;
- static final int MSG_SHUTDOWN = 2;
- static final int MSG_SOUND_MODELS_CHANGED = 3;
- static final int MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD = 4;
-
- class MyHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_READY:
- onReady();
- break;
- case MSG_SHUTDOWN:
- onShutdownInternal();
- break;
- case MSG_SOUND_MODELS_CHANGED:
- onSoundModelsChangedInternal();
- break;
- case MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD:
- onLaunchVoiceAssistFromKeyguard();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
-
/**
* Called when a user has activated an affordance to launch voice assist from the Keyguard.
*
@@ -186,7 +186,7 @@
* be any combination of
* {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
* {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
- * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
+ * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
* to request that the system generate and deliver assist data on the current foreground
* app as part of showing the session UI.
*/
@@ -200,10 +200,22 @@
}
}
- @Override
- public void onCreate() {
- super.onCreate();
- mHandler = new MyHandler();
+ /**
+ * Request to query for what extended voice actions this service supports. This method will
+ * be called when the system checks the supported actions of this
+ * {@link VoiceInteractionService}. Supported actions may be delivered to
+ * {@link VoiceInteractionSession} later to request a session to perform an action.
+ *
+ * <p>Voice actions are defined in support libraries and could vary based on platform context.
+ * For example, car related voice actions will be defined in car support libraries.
+ *
+ * @param voiceActions A set of checked voice actions.
+ * @return Returns a subset of checked voice actions. Additional voice actions in the
+ * returned set will be ignored. Returns null or empty set if no actions are supported.
+ */
+ @Nullable
+ public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
+ return null;
}
@Override
@@ -254,6 +266,18 @@
}
}
+ private void onHandleVoiceActionCheck(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ if (callback != null) {
+ try {
+ Set<String> voiceActionsSet = new ArraySet<>(voiceActions);
+ Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet);
+ callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
* This instance must be retained and used by the client.
@@ -289,12 +313,12 @@
}
/**
- * Checks if a given keyphrase and locale are supported to create an
- * {@link AlwaysOnHotwordDetector}.
- *
- * @return true if the keyphrase and locale combination is supported, false otherwise.
- * @hide
- */
+ * Checks if a given keyphrase and locale are supported to create an
+ * {@link AlwaysOnHotwordDetector}.
+ *
+ * @return true if the keyphrase and locale combination is supported, false otherwise.
+ * @hide
+ */
@UnsupportedAppUsage
public final boolean isKeyphraseAndLocaleSupportedForHotword(String keyphrase, Locale locale) {
if (mKeyphraseEnrollmentInfo == null) {
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 26223f7..163e3d5 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -115,6 +115,12 @@
*/
public static final int SHOW_SOURCE_PUSH_TO_TALK = 1 << 5;
+ /**
+ * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
+ * from a notification.
+ */
+ public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6;
+
final Context mContext;
final HandlerCaller mHandlerCaller;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 33c977b..c89617f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -619,7 +619,7 @@
previousLineBottom = lbottom;
int lbaseline = lbottom - getLineDescent(i);
- if (start >= spanEnd) {
+ if (end >= spanEnd) {
// These should be infrequent, so we'll use this so that
// we don't have to check as often.
spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength);
diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java
index 2bcfa5f..94e10e8 100644
--- a/core/java/android/text/NativeLineBreaker.java
+++ b/core/java/android/text/NativeLineBreaker.java
@@ -21,6 +21,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Px;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -258,16 +259,91 @@
/**
* A result object of a line breaking
*/
- public static class LineBreaks {
- public int breakCount;
- private static final int INITIAL_SIZE = 16;
- public int[] breaks = new int[INITIAL_SIZE];
- public float[] widths = new float[INITIAL_SIZE];
- public float[] ascents = new float[INITIAL_SIZE];
- public float[] descents = new float[INITIAL_SIZE];
- // TODO: Introduce Hyphenator for explaining the meaning of flags.
- public int[] flags = new int[INITIAL_SIZE];
- // breaks, widths, and flags should all have the same length
+ public static class Result {
+ // Following two contstant must be synced with minikin's line breaker.
+ private static final int TAB_MASK = 0x20000000;
+ private static final int HYPHEN_MASK = 0xFF;
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ Result.class.getClassLoader(), nGetReleaseResultFunc(), 32);
+ private final long mPtr;
+
+ private Result(long ptr) {
+ mPtr = ptr;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ /**
+ * Returns a number of line count.
+ *
+ * @return number of lines
+ */
+ public @IntRange(from = 0) int getLineCount() {
+ return nGetLineCount(mPtr);
+ }
+
+ /**
+ * Returns a break offset of the line.
+ *
+ * @param lineIndex an index of the line.
+ * @return the break offset.
+ */
+ public @IntRange(from = 0) int getLineBreakOffset(@IntRange(from = 0) int lineIndex) {
+ return nGetLineBreakOffset(mPtr, lineIndex);
+ }
+
+ /**
+ * Returns a width of the line in pixels.
+ *
+ * @param lineIndex an index of the line.
+ * @return a width of the line in pixexls
+ */
+ public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) {
+ return nGetLineWidth(mPtr, lineIndex);
+ }
+
+ /**
+ * Returns an entier font ascent of the line in pixels.
+ *
+ * @param lineIndex an index of the line.
+ * @return an entier font ascent of the line in pixels.
+ */
+ public @Px float getLineAscent(@IntRange(from = 0) int lineIndex) {
+ return nGetLineAscent(mPtr, lineIndex);
+ }
+
+ /**
+ * Returns an entier font descent of the line in pixels.
+ *
+ * @param lineIndex an index of the line.
+ * @return an entier font descent of the line in pixels.
+ */
+ public @Px float getLineDescent(@IntRange(from = 0) int lineIndex) {
+ return nGetLineDescent(mPtr, lineIndex);
+ }
+
+ /**
+ * Returns true if the line has a TAB character.
+ *
+ * @param lineIndex an index of the line.
+ * @return true if the line has a TAB character
+ */
+ public boolean hasLineTab(int lineIndex) {
+ return (nGetLineFlag(mPtr, lineIndex) & TAB_MASK) != 0;
+ }
+
+ /**
+ * Returns a packed packed hyphen edit for the line.
+ *
+ * @param lineIndex an index of the line.
+ * @return a packed hyphen edit for the line.
+ * @see android.text.Hyphenator#unpackStartHyphenEdit(int)
+ * @see android.text.Hyphenator#unpackEndHyphenEdit(int)
+ * @see android.text.Hyphenator#packHyphenEdit(int,int)
+ */
+ public int getLineHyphenEdit(int lineIndex) {
+ return (nGetLineFlag(mPtr, lineIndex) & HYPHEN_MASK);
+ }
}
private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
@@ -294,14 +370,12 @@
* @param measuredPara a result of the text measurement
* @param constraints for a single paragraph
* @param lineNumber a line number of this paragraph
- * @param out object to set line break information for the given paragraph
*/
- public void computeLineBreaks(
+ public Result computeLineBreaks(
@NonNull NativeMeasuredParagraph measuredPara,
@NonNull ParagraphConstraints constraints,
- @IntRange(from = 0) int lineNumber,
- @NonNull LineBreaks out) {
- out.breakCount = nComputeLineBreaks(
+ @IntRange(from = 0) int lineNumber) {
+ return new Result(nComputeLineBreaks(
mNativePtr,
// Inputs
@@ -313,17 +387,7 @@
constraints.mWidth,
constraints.mVariableTabStops,
constraints.mDefaultTabStop,
- lineNumber,
-
- // Outputs
- out,
- out.breaks.length,
- out.breaks,
- out.widths,
- out.ascents,
- out.descents,
- out.flags);
-
+ lineNumber));
}
@FastNative
@@ -341,7 +405,7 @@
// arrays do not have to be resized
// The individual character widths will be returned in charWidths. The length of
// charWidths must be at least the length of the text.
- private static native int nComputeLineBreaks(
+ private static native long nComputeLineBreaks(
/* non zero */ long nativePtr,
// Inputs
@@ -353,14 +417,21 @@
@FloatRange(from = 0.0f) float restWidth,
@Nullable int[] variableTabStops,
int defaultTabStop,
- @IntRange(from = 0) int indentsOffset,
+ @IntRange(from = 0) int indentsOffset);
- // Outputs
- @NonNull LineBreaks recycle,
- @IntRange(from = 0) int recycleLength,
- @NonNull int[] recycleBreaks,
- @NonNull float[] recycleWidths,
- @NonNull float[] recycleAscents,
- @NonNull float[] recycleDescents,
- @NonNull int[] recycleFlags);
+ // Result accessors
+ @CriticalNative
+ private static native int nGetLineCount(long ptr);
+ @CriticalNative
+ private static native int nGetLineBreakOffset(long ptr, int idx);
+ @CriticalNative
+ private static native float nGetLineWidth(long ptr, int idx);
+ @CriticalNative
+ private static native float nGetLineAscent(long ptr, int idx);
+ @CriticalNative
+ private static native float nGetLineDescent(long ptr, int idx);
+ @CriticalNative
+ private static native int nGetLineFlag(long ptr, int idx);
+ @CriticalNative
+ private static native long nGetReleaseResultFunc();
}
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index c5fabaf..0d29da0 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1610,13 +1610,14 @@
public boolean equals(Object o) {
if (o instanceof Spanned &&
toString().equals(o.toString())) {
- Spanned other = (Spanned) o;
+ final Spanned other = (Spanned) o;
// Check span data
- Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
+ final Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
+ final Object[] thisSpans = getSpans(0, length(), Object.class);
if (mSpanCount == otherSpans.length) {
for (int i = 0; i < mSpanCount; ++i) {
- Object thisSpan = mSpans[i];
- Object otherSpan = otherSpans[i];
+ final Object thisSpan = thisSpans[i];
+ final Object otherSpan = otherSpans[i];
if (thisSpan == this) {
if (other != otherSpan ||
getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 7acd539..a986633 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -16,12 +16,13 @@
package android.text;
+import android.annotation.UnsupportedAppUsage;
+
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import libcore.util.EmptyArray;
-import android.annotation.UnsupportedAppUsage;
import java.lang.reflect.Array;
/* package */ abstract class SpannableStringInternal
@@ -502,13 +503,14 @@
public boolean equals(Object o) {
if (o instanceof Spanned &&
toString().equals(o.toString())) {
- Spanned other = (Spanned) o;
+ final Spanned other = (Spanned) o;
// Check span data
- Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
+ final Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
+ final Object[] thisSpans = getSpans(0, length(), Object.class);
if (mSpanCount == otherSpans.length) {
for (int i = 0; i < mSpanCount; ++i) {
- Object thisSpan = mSpans[i];
- Object otherSpan = otherSpans[i];
+ final Object thisSpan = thisSpans[i];
+ final Object otherSpan = otherSpans[i];
if (thisSpan == this) {
if (other != otherSpan ||
getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e1ffef0..d2f0853 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -599,7 +599,14 @@
float ellipsizedWidth = b.mEllipsizedWidth;
TextUtils.TruncateAt ellipsize = b.mEllipsize;
final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
- NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks();
+
+ int lineBreakCapacity = 0;
+ int[] breaks = null;
+ float[] lineWidths = null;
+ float[] ascents = null;
+ float[] descents = null;
+ boolean[] hasTabs = null;
+ int[] hyphenEdits = null;
mLineCount = 0;
mEllipsized = false;
@@ -732,14 +739,27 @@
constraints.setIndent(firstWidth, firstWidthLineCount);
constraints.setTabStops(variableTabStops, TAB_INCREMENT);
- lineBreaker.computeLineBreaks(measuredPara.getNativeMeasuredParagraph(),
- constraints, mLineCount, lineBreaks);
- int breakCount = lineBreaks.breakCount;
- final int[] breaks = lineBreaks.breaks;
- final float[] lineWidths = lineBreaks.widths;
- final float[] ascents = lineBreaks.ascents;
- final float[] descents = lineBreaks.descents;
- final int[] flags = lineBreaks.flags;
+ NativeLineBreaker.Result res = lineBreaker.computeLineBreaks(
+ measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount);
+ int breakCount = res.getLineCount();
+ if (lineBreakCapacity < breakCount) {
+ lineBreakCapacity = breakCount;
+ breaks = new int[lineBreakCapacity];
+ lineWidths = new float[lineBreakCapacity];
+ ascents = new float[lineBreakCapacity];
+ descents = new float[lineBreakCapacity];
+ hasTabs = new boolean[lineBreakCapacity];
+ hyphenEdits = new int[lineBreakCapacity];
+ }
+
+ for (int i = 0; i < breakCount; ++i) {
+ breaks[i] = res.getLineBreakOffset(i);
+ lineWidths[i] = res.getLineWidth(i);
+ ascents[i] = res.getLineAscent(i);
+ descents[i] = res.getLineDescent(i);
+ hasTabs[i] = res.hasLineTab(i);
+ hyphenEdits[i] = res.getLineHyphenEdit(i);
+ }
final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
final boolean ellipsisMayBeApplied = ellipsize != null
@@ -750,7 +770,7 @@
&& ellipsisMayBeApplied) {
// Calculate width
float width = 0;
- int flag = 0; // XXX May need to also have starting hyphen edit
+ boolean hasTab = false; // XXX May need to also have starting hyphen edit
for (int i = remainingLineCount - 1; i < breakCount; i++) {
if (i == breakCount - 1) {
width += lineWidths[i];
@@ -759,12 +779,12 @@
width += measuredPara.getCharWidthAt(j - paraStart);
}
}
- flag |= flags[i] & TAB_MASK;
+ hasTab |= hasTabs[i];
}
// Treat the last line and overflowed lines as a single line.
breaks[remainingLineCount - 1] = breaks[breakCount - 1];
lineWidths[remainingLineCount - 1] = width;
- flags[remainingLineCount - 1] = flag;
+ hasTabs[remainingLineCount - 1] = hasTab;
breakCount = remainingLineCount;
}
@@ -821,8 +841,8 @@
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
- flags[breakIndex], needMultiply, measuredPara, bufEnd,
- includepad, trackpad, addLastLineSpacing, chs,
+ hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply,
+ measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs,
paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
paint, moreChars);
@@ -860,7 +880,7 @@
fm.top, fm.bottom,
v,
spacingmult, spacingadd, null,
- null, fm, 0,
+ null, fm, false, 0,
needMultiply, measuredPara, bufEnd,
includepad, trackpad, addLastLineSpacing, null,
bufStart, ellipsize,
@@ -871,7 +891,8 @@
private int out(final CharSequence text, final int start, final int end, int above, int below,
int top, int bottom, int v, final float spacingmult, final float spacingadd,
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
- final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured,
+ final boolean hasTab, final int hyphenEdit, final boolean needMultiply,
+ @NonNull final MeasuredParagraph measured,
final int bufEnd, final boolean includePad, final boolean trackPad,
final boolean addLastLineLineSpacing, final char[] chs,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
@@ -1005,8 +1026,8 @@
// TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
// one bit for start field
- lines[off + TAB] |= flags & TAB_MASK;
- lines[off + HYPHEN] = flags;
+ lines[off + TAB] |= hasTab ? TAB_MASK : 0;
+ lines[off + HYPHEN] = hyphenEdit;
lines[off + DIR] |= dir << DIR_SHIFT;
mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index b49a949..44dfd11 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -1230,7 +1230,7 @@
// with the next chunk. So we just save the TextPaint for future comparisons
// and use.
activePaint.set(wp);
- } else if (!wp.hasEqualAttributes(activePaint)) {
+ } else if (!equalAttributes(wp, activePaint)) {
// The style of the present chunk of text is substantially different from the
// style of the previous chunk. We need to handle the active piece of text
// and restart with the present chunk.
@@ -1335,4 +1335,42 @@
}
private static final int TAB_INCREMENT = 20;
+
+ private static boolean equalAttributes(@NonNull TextPaint lp, @NonNull TextPaint rp) {
+ return lp.getColorFilter() == rp.getColorFilter()
+ && lp.getMaskFilter() == rp.getMaskFilter()
+ && lp.getShader() == rp.getShader()
+ && lp.getTypeface() == rp.getTypeface()
+ && lp.getXfermode() == rp.getXfermode()
+ && lp.getTextLocales().equals(rp.getTextLocales())
+ && TextUtils.equals(lp.getFontFeatureSettings(), rp.getFontFeatureSettings())
+ && TextUtils.equals(lp.getFontVariationSettings(), rp.getFontVariationSettings())
+ && lp.getShadowLayerRadius() == rp.getShadowLayerRadius()
+ && lp.getShadowLayerDx() == rp.getShadowLayerDx()
+ && lp.getShadowLayerDy() == rp.getShadowLayerDy()
+ && lp.getShadowLayerColor() == rp.getShadowLayerColor()
+ && lp.getFlags() == rp.getFlags()
+ && lp.getHinting() == rp.getHinting()
+ && lp.getStyle() == rp.getStyle()
+ && lp.getColor() == rp.getColor()
+ && lp.getStrokeWidth() == rp.getStrokeWidth()
+ && lp.getStrokeMiter() == rp.getStrokeMiter()
+ && lp.getStrokeCap() == rp.getStrokeCap()
+ && lp.getStrokeJoin() == rp.getStrokeJoin()
+ && lp.getTextAlign() == rp.getTextAlign()
+ && lp.isElegantTextHeight() == rp.isElegantTextHeight()
+ && lp.getTextSize() == rp.getTextSize()
+ && lp.getTextScaleX() == rp.getTextScaleX()
+ && lp.getTextSkewX() == rp.getTextSkewX()
+ && lp.getLetterSpacing() == rp.getLetterSpacing()
+ && lp.getWordSpacing() == rp.getWordSpacing()
+ && lp.getHyphenEdit() == rp.getHyphenEdit()
+ && lp.bgColor == rp.bgColor
+ && lp.baselineShift == rp.baselineShift
+ && lp.linkColor == rp.linkColor
+ && lp.drawableState == rp.drawableState
+ && lp.density == rp.density
+ && lp.underlineColor == rp.underlineColor
+ && lp.underlineThickness == rp.underlineThickness;
+ }
}
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index 7bcc685..d5aad33 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -17,7 +17,7 @@
package android.text;
import android.annotation.ColorInt;
-import android.annotation.NonNull;
+import android.annotation.Px;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
@@ -37,17 +37,14 @@
public float density = 1.0f;
/**
* Special value 0 means no custom underline
- * @hide
*/
@ColorInt
- @UnsupportedAppUsage
public int underlineColor = 0;
+
/**
* Thickness of the underline, in pixels.
- * @hide
*/
- @UnsupportedAppUsage
- public float underlineThickness;
+ public @Px float underlineThickness;
public TextPaint() {
super();
@@ -78,24 +75,6 @@
}
/**
- * Returns true if all attributes, including the attributes inherited from Paint, are equal.
- *
- * The caller is expected to have checked the trivial cases, like the pointers being equal,
- * the objects having different classes, or the parameter being null.
- * @hide
- */
- public boolean hasEqualAttributes(@NonNull TextPaint other) {
- return bgColor == other.bgColor
- && baselineShift == other.baselineShift
- && linkColor == other.linkColor
- && drawableState == other.drawableState
- && density == other.density
- && underlineColor == other.underlineColor
- && underlineThickness == other.underlineThickness
- && super.hasEqualAttributes((Paint) other);
- }
-
- /**
* Defines a custom underline for this Paint.
* @param color underline solid color
* @param thickness underline thickness
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index c17cfd5..2dc4f60 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -16,11 +16,13 @@
package android.text.style;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.LeakyTypefaceStorage;
import android.graphics.Typeface;
+import android.graphics.fonts.Font;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
@@ -38,6 +40,21 @@
private final ColorStateList mTextColorLink;
private final Typeface mTypeface;
+ private final int mTextFontWeight;
+
+ private final float mShadowRadius;
+ private final float mShadowDx;
+ private final float mShadowDy;
+ private final int mShadowColor;
+
+ private final boolean mHasElegantTextHeight;
+ private final boolean mElegantTextHeight;
+ private final boolean mHasLetterSpacing;
+ private final float mLetterSpacing;
+
+ private final String mFontFeatureSettings;
+ private final String mFontVariationSettings;
+
/**
* Uses the specified TextAppearance resource to determine the
* text appearance. The <code>appearance</code> should be, for example,
@@ -104,6 +121,34 @@
}
}
+ mTextFontWeight = a.getInt(com.android.internal.R.styleable
+ .TextAppearance_textFontWeight, -1);
+
+ mShadowRadius = a.getFloat(com.android.internal.R.styleable
+ .TextAppearance_shadowRadius, 0.0f);
+ mShadowDx = a.getFloat(com.android.internal.R.styleable
+ .TextAppearance_shadowDx, 0.0f);
+ mShadowDy = a.getFloat(com.android.internal.R.styleable
+ .TextAppearance_shadowDy, 0.0f);
+ mShadowColor = a.getInt(com.android.internal.R.styleable
+ .TextAppearance_shadowColor, 0);
+
+ mHasElegantTextHeight = a.hasValue(com.android.internal.R.styleable
+ .TextAppearance_elegantTextHeight);
+ mElegantTextHeight = a.getBoolean(com.android.internal.R.styleable
+ .TextAppearance_elegantTextHeight, false);
+
+ mHasLetterSpacing = a.hasValue(com.android.internal.R.styleable
+ .TextAppearance_letterSpacing);
+ mLetterSpacing = a.getFloat(com.android.internal.R.styleable
+ .TextAppearance_letterSpacing, 0.0f);
+
+ mFontFeatureSettings = a.getString(com.android.internal.R.styleable
+ .TextAppearance_fontFeatureSettings);
+
+ mFontVariationSettings = a.getString(com.android.internal.R.styleable
+ .TextAppearance_fontVariationSettings);
+
a.recycle();
if (colorList >= 0) {
@@ -129,6 +174,21 @@
mTextColor = color;
mTextColorLink = linkColor;
mTypeface = null;
+
+ mTextFontWeight = -1;
+
+ mShadowRadius = 0.0f;
+ mShadowDx = 0.0f;
+ mShadowDy = 0.0f;
+ mShadowColor = 0;
+
+ mHasElegantTextHeight = false;
+ mElegantTextHeight = false;
+ mHasLetterSpacing = false;
+ mLetterSpacing = 0.0f;
+
+ mFontFeatureSettings = null;
+ mFontVariationSettings = null;
}
public TextAppearanceSpan(Parcel src) {
@@ -146,6 +206,21 @@
mTextColorLink = null;
}
mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
+
+ mTextFontWeight = src.readInt();
+
+ mShadowRadius = src.readFloat();
+ mShadowDx = src.readFloat();
+ mShadowDy = src.readFloat();
+ mShadowColor = src.readInt();
+
+ mHasElegantTextHeight = src.readBoolean();
+ mElegantTextHeight = src.readBoolean();
+ mHasLetterSpacing = src.readBoolean();
+ mLetterSpacing = src.readFloat();
+
+ mFontFeatureSettings = src.readString();
+ mFontVariationSettings = src.readString();
}
public int getSpanTypeId() {
@@ -183,6 +258,21 @@
dest.writeInt(0);
}
LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
+
+ dest.writeInt(mTextFontWeight);
+
+ dest.writeFloat(mShadowRadius);
+ dest.writeFloat(mShadowDx);
+ dest.writeFloat(mShadowDy);
+ dest.writeInt(mShadowColor);
+
+ dest.writeBoolean(mHasElegantTextHeight);
+ dest.writeBoolean(mElegantTextHeight);
+ dest.writeBoolean(mHasLetterSpacing);
+ dest.writeFloat(mLetterSpacing);
+
+ dest.writeString(mFontFeatureSettings);
+ dest.writeString(mFontVariationSettings);
}
/**
@@ -225,6 +315,81 @@
return mStyle;
}
+ /**
+ * Returns the text font weight specified by this span, or <code>-1</code>
+ * if it does not specify one.
+ */
+ public int getTextFontWeight() {
+ return mTextFontWeight;
+ }
+
+ /**
+ * Returns the typeface specified by this span, or <code>null</code>
+ * if it does not specify one.
+ */
+ @Nullable
+ public Typeface getTypeface() {
+ return mTypeface;
+ }
+
+ /**
+ * Returns the color of the text shadow specified by this span, or <code>0</code>
+ * if it does not specify one.
+ */
+ public int getShadowColor() {
+ return mShadowColor;
+ }
+
+ /**
+ * Returns the horizontal offset of the text shadow specified by this span, or <code>0.0f</code>
+ * if it does not specify one.
+ */
+ public float getShadowDx() {
+ return mShadowDx;
+ }
+
+ /**
+ * Returns the vertical offset of the text shadow specified by this span, or <code>0.0f</code>
+ * if it does not specify one.
+ */
+ public float getShadowDy() {
+ return mShadowDy;
+ }
+
+ /**
+ * Returns the blur radius of the text shadow specified by this span, or <code>0.0f</code>
+ * if it does not specify one.
+ */
+ public float getShadowRadius() {
+ return mShadowRadius;
+ }
+
+ /**
+ * Returns the font feature settings specified by this span, or <code>null</code>
+ * if it does not specify one.
+ */
+ @Nullable
+ public String getFontFeatureSettings() {
+ return mFontFeatureSettings;
+ }
+
+ /**
+ * Returns the font variation settings specified by this span, or <code>null</code>
+ * if it does not specify one.
+ */
+ @Nullable
+ public String getFontVariationSettings() {
+ return mFontVariationSettings;
+ }
+
+ /**
+ * Returns the value of elegant height metrics flag specified by this span,
+ * or <code>false</code> if it does not specify one.
+ */
+ public boolean isElegantTextHeight() {
+ return mElegantTextHeight;
+ }
+
@Override
public void updateDrawState(TextPaint ds) {
updateMeasureState(ds);
@@ -236,6 +401,10 @@
if (mTextColorLink != null) {
ds.linkColor = mTextColorLink.getColorForState(ds.drawableState, 0);
}
+
+ if (mShadowColor != 0) {
+ ds.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
+ }
}
@Override
@@ -267,7 +436,16 @@
}
if (styledTypeface != null) {
- int fake = style & ~styledTypeface.getStyle();
+ final Typeface readyTypeface;
+ if (mTextFontWeight >= 0) {
+ final int weight = Math.min(Font.FONT_WEIGHT_MAX, mTextFontWeight);
+ final boolean italic = (style & Typeface.ITALIC) != 0;
+ readyTypeface = ds.setTypeface(Typeface.create(styledTypeface, weight, italic));
+ } else {
+ readyTypeface = styledTypeface;
+ }
+
+ int fake = style & ~readyTypeface.getStyle();
if ((fake & Typeface.BOLD) != 0) {
ds.setFakeBoldText(true);
@@ -277,11 +455,27 @@
ds.setTextSkewX(-0.25f);
}
- ds.setTypeface(styledTypeface);
+ ds.setTypeface(readyTypeface);
}
if (mTextSize > 0) {
ds.setTextSize(mTextSize);
}
+
+ if (mHasElegantTextHeight) {
+ ds.setElegantTextHeight(mElegantTextHeight);
+ }
+
+ if (mHasLetterSpacing) {
+ ds.setLetterSpacing(mLetterSpacing);
+ }
+
+ if (mFontFeatureSettings != null) {
+ ds.setFontFeatureSettings(mFontFeatureSettings);
+ }
+
+ if (mFontVariationSettings != null) {
+ ds.setFontVariationSettings(mFontVariationSettings);
+ }
}
}
diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java
index d7a9120..9fa9961 100644
--- a/core/java/android/transition/ChangeImageTransform.java
+++ b/core/java/android/transition/ChangeImageTransform.java
@@ -97,22 +97,13 @@
values.put(PROPNAME_BOUNDS, bounds);
Matrix matrix;
ImageView.ScaleType scaleType = imageView.getScaleType();
- if (scaleType == ImageView.ScaleType.FIT_XY) {
- matrix = imageView.getImageMatrix();
- if (!matrix.isIdentity()) {
- matrix = new Matrix(matrix);
- } else {
- int drawableWidth = drawable.getIntrinsicWidth();
- int drawableHeight = drawable.getIntrinsicHeight();
- if (drawableWidth > 0 && drawableHeight > 0) {
- float scaleX = ((float) bounds.width()) / drawableWidth;
- float scaleY = ((float) bounds.height()) / drawableHeight;
- matrix = new Matrix();
- matrix.setScale(scaleX, scaleY);
- } else {
- matrix = null;
- }
- }
+ int drawableWidth = drawable.getIntrinsicWidth();
+ int drawableHeight = drawable.getIntrinsicHeight();
+ if (scaleType == ImageView.ScaleType.FIT_XY && drawableWidth > 0 && drawableHeight > 0) {
+ float scaleX = ((float) bounds.width()) / drawableWidth;
+ float scaleY = ((float) bounds.height()) / drawableHeight;
+ matrix = new Matrix();
+ matrix.setScale(scaleX, scaleY);
} else {
matrix = new Matrix(imageView.getImageMatrix());
}
@@ -152,17 +143,13 @@
}
Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- if (startBounds == null || endBounds == null) {
+ Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+ Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+ if (startBounds == null || endBounds == null || startMatrix == null || endMatrix == null) {
return null;
}
- Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
- Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
-
- boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
- (startMatrix != null && startMatrix.equals(endMatrix));
-
- if (startBounds.equals(endBounds) && matricesEqual) {
+ if (startBounds.equals(endBounds) && startMatrix.equals(endMatrix)) {
return null;
}
@@ -172,15 +159,9 @@
int drawableHeight = drawable.getIntrinsicHeight();
ObjectAnimator animator;
- if (drawableWidth == 0 || drawableHeight == 0) {
+ if (drawableWidth <= 0 || drawableHeight <= 0) {
animator = createNullAnimator(imageView);
} else {
- if (startMatrix == null) {
- startMatrix = Matrix.IDENTITY_MATRIX;
- }
- if (endMatrix == null) {
- endMatrix = Matrix.IDENTITY_MATRIX;
- }
ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
}
@@ -189,7 +170,7 @@
private ObjectAnimator createNullAnimator(ImageView imageView) {
return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
- NULL_MATRIX_EVALUATOR, null, null);
+ NULL_MATRIX_EVALUATOR, Matrix.IDENTITY_MATRIX, Matrix.IDENTITY_MATRIX);
}
private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index f8e8762..4608e20 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -188,7 +188,13 @@
final ViewGroup sceneRoot = scene.getSceneRoot();
if (!sPendingTransitions.contains(sceneRoot)) {
+ Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (transition == null) {
+ // Notify old scene that it is being exited
+ if (oldScene != null) {
+ oldScene.exit();
+ }
+
scene.enter();
} else {
sPendingTransitions.add(sceneRoot);
@@ -196,7 +202,6 @@
Transition transitionClone = transition.clone();
transitionClone.setSceneRoot(sceneRoot);
- Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
transitionClone.setCanRemoveViews(true);
}
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 5108a79..294a179 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -71,19 +71,19 @@
/**
* Maximum number of entries to have in array caches.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private static final int CACHE_SIZE = 10;
/**
* Special hash array value that indicates the container is immutable.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
/**
* @hide Special immutable empty ArrayMap.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use your own singleton empty map.
public static final ArrayMap EMPTY = new ArrayMap<>(-1);
/**
@@ -92,21 +92,21 @@
* The first entry in the array is a pointer to the next array in the
* list; the second entry is a pointer to the int[] hash code array for it.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static Object[] mBaseCache;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static int mBaseCacheSize;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static Object[] mTwiceBaseCache;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static int mTwiceBaseCacheSize;
final boolean mIdentityHashCode;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public key/value API.
int[] mHashes;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public key/value API.
Object[] mArray;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
int mSize;
MapCollections<K, V> mCollections;
@@ -122,7 +122,7 @@
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
int indexOf(Object key, int hash) {
final int N = mSize;
@@ -161,7 +161,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null)
int indexOfNull() {
final int N = mSize;
@@ -200,7 +200,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private void allocArrays(final int size) {
if (mHashes == EMPTY_IMMUTABLE_INTS) {
throw new UnsupportedOperationException("ArrayMap is immutable");
@@ -239,7 +239,7 @@
mArray = new Object[size<<1];
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
if (hashes.length == (BASE_SIZE*2)) {
synchronized (ArrayMap.class) {
@@ -393,8 +393,15 @@
: indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
}
- @UnsupportedAppUsage
- int indexOfValue(Object value) {
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified value, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(Object value) {
final int N = mSize*2;
final Object[] array = mArray;
if (value == null) {
@@ -551,7 +558,7 @@
* The array must already be large enough to contain the item.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use put(K, V).
public void append(K key, V value) {
int index = mSize;
final int hash = key == null ? 0
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 526a950..d74a0fe 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -71,15 +71,15 @@
static int sTwiceBaseCacheSize;
final boolean mIdentityHashCode;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public API.
int[] mHashes;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public API.
Object[] mArray;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
int mSize;
MapCollections<E, E> mCollections;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
private int indexOf(Object key, int hash) {
final int N = mSize;
@@ -118,7 +118,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null)
private int indexOfNull() {
final int N = mSize;
@@ -157,7 +157,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private void allocArrays(final int size) {
if (size == (BASE_SIZE * 2)) {
synchronized (ArraySet.class) {
@@ -215,7 +215,7 @@
mArray = new Object[size];
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
if (hashes.length == (BASE_SIZE * 2)) {
synchronized (ArraySet.class) {
@@ -289,9 +289,10 @@
}
}
- /** {@hide} */
- @UnsupportedAppUsage
- public ArraySet(Collection<E> set) {
+ /**
+ * Create a new ArraySet with items from the given collection.
+ */
+ public ArraySet(Collection<? extends E> set) {
this();
if (set != null) {
addAll(set);
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index d5af922..af163ac 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -46,11 +46,11 @@
* @hide
*/
public class LongSparseLongArray implements Cloneable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
private long[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
private long[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
private int mSize;
/**
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index aa5ca353..89ea2d3 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -56,11 +56,11 @@
private static final Object DELETED = new Object();
private boolean mGarbage = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
private int[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, E)
private Object[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
private int mSize;
/**
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 9c6b969..d4c4095 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -185,7 +185,9 @@
return mValues[index];
}
- /** @hide */
+ /**
+ * Directly set the value at a particular index.
+ */
public void setValueAt(int index, boolean value) {
mValues[index] = value;
}
@@ -304,10 +306,10 @@
return buffer.toString();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
private int[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, boolean)
private boolean[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
private int mSize;
}
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 1954753..9e6bad1 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -46,11 +46,11 @@
* order in the case of <code>valueAt(int)</code>.</p>
*/
public class SparseIntArray implements Cloneable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
private int[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, int)
private int[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
private int mSize;
/**
@@ -191,7 +191,6 @@
/**
* Directly set the value at a particular index.
- * @hide
*/
public void setValueAt(int index, int value) {
mValues[index] = value;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 533d725..1bbef8e9 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,11 +410,11 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
- static byte[] generateFsverityRootHash(String apkPath)
+ static byte[] generateApkVerityRootHash(String apkPath)
throws IOException, SignatureNotFoundException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -423,7 +423,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateFsverityRootHash(
+ return VerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 758cd2b..1471870 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,11 +534,11 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
- static byte[] generateFsverityRootHash(String apkPath)
+ static byte[] generateApkVerityRootHash(String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException,
SignatureNotFoundException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -547,7 +547,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateFsverityRootHash(
+ return VerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index de9f55b..a299b11 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -432,16 +432,16 @@
*
* @return FSverity root hash
*/
- public static byte[] generateFsverityRootHash(String apkPath)
+ public static byte[] generateApkVerityRootHash(String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
// first try v3
try {
- return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
// try older version
}
try {
- return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
return null;
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index e247c87..87af5364 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -332,7 +332,7 @@
try {
byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
apk.length(), signatureInfo);
- ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
+ VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
public ByteBuffer create(int capacity) {
@@ -348,26 +348,6 @@
}
/**
- * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
- * method does not check whether the root hash exists in the Signing Block or not.
- *
- * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
- * ByteBufferFactory}.
- *
- * @return the root hash of the generated hash tree.
- */
- public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
- SignatureInfo signatureInfo)
- throws IOException, SignatureNotFoundException, SecurityException, DigestException,
- NoSuchAlgorithmException {
- try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
- ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
- signatureInfo, bufferFactory);
- return result.rootHash;
- }
- }
-
- /**
* Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
*
* @throws IOException if an I/O error occurs while reading the file.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
similarity index 65%
rename from core/java/android/util/apk/ApkVerityBuilder.java
rename to core/java/android/util/apk/VerityBuilder.java
index 2dd0117..443bbd8 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -16,6 +16,9 @@
package android.util.apk;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -26,17 +29,18 @@
import java.util.ArrayList;
/**
- * ApkVerityBuilder builds the APK verity tree and the verity header, which will be used by the
- * kernel to verity the APK content on access.
+ * VerityBuilder builds the verity Merkle tree and other metadata. The generated tree format can
+ * be stored on disk for fs-verity setup and used by kernel. The builder support standard
+ * fs-verity, and Android specific apk-verity that requires additional kernel patches.
*
- * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
- * the existing APK format, it has to skip APK Signing Block and also has some special treatment for
- * the "Central Directory offset" field of ZIP End of Central Directory.
+ * <p>Unlike a regular Merkle tree of fs-verity, the apk-verity tree does not cover the file content
+ * fully, and has to skip APK Signing Block with some special treatment for the "Central Directory
+ * offset" field of ZIP End of Central Directory.
*
* @hide
*/
-abstract class ApkVerityBuilder {
- private ApkVerityBuilder() {}
+public abstract class VerityBuilder {
+ private VerityBuilder() {}
private static final int CHUNK_SIZE_BYTES = 4096; // Typical Linux block size
private static final int DIGEST_SIZE_BYTES = 32; // SHA-256 size
@@ -46,116 +50,138 @@
private static final String JCA_DIGEST_ALGORITHM = "SHA-256";
private static final byte[] DEFAULT_SALT = new byte[8];
- static class ApkVerityResult {
- public final ByteBuffer fsverityData;
+ /** Result generated by the builder. */
+ public static class VerityResult {
+ /** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */
+ public final ByteBuffer verityData;
+
+ /** Size of the Merkle tree in {@code verityData}. */
+ public final int merkleTreeSize;
+
+ /** Root hash of the Merkle tree. */
public final byte[] rootHash;
- ApkVerityResult(ByteBuffer fsverityData, byte[] rootHash) {
- this.fsverityData = fsverityData;
+ private VerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+ this.verityData = verityData;
+ this.merkleTreeSize = merkleTreeSize;
this.rootHash = rootHash;
}
}
/**
- * Generates fsverity metadata and the Merkle tree into the {@link ByteBuffer} created by the
- * {@link ByteBufferFactory}. The bytes layout in the buffer will be used by the kernel and is
- * ready to be appended to the target file to set up fsverity. For fsverity to work, this data
- * must be placed at the next page boundary, and the caller must add additional padding in that
- * case.
+ * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
+ * ByteBuffer} created by the {@link ByteBufferFactory}. The output is suitable to be used as
+ * the on-disk format for fs-verity to use.
*
- * @return ApkVerityResult containing the fsverity data and the root hash of the Merkle tree.
+ * @return VerityResult containing a buffer with the generated Merkle tree stored at the
+ * front, the tree size, and the calculated root hash.
*/
- static ApkVerityResult generateApkVerity(RandomAccessFile apk,
- SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
+ @NonNull
+ public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
+ @NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
- long signingBlockSize =
- signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
- long dataSize = apk.length() - signingBlockSize;
+ return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
+ false /* skipSigningBlock */);
+ }
+
+ /**
+ * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
+ * ByteBuffer} created by the {@link ByteBufferFactory}. The Merkle tree does not cover Signing
+ * Block specificed in {@code signatureInfo}. The output is suitable to be used as the on-disk
+ * format for fs-verity to use (with elide and patch extensions).
+ *
+ * @return VerityResult containing a buffer with the generated Merkle tree stored at the
+ * front, the tree size, and the calculated root hash.
+ */
+ @NonNull
+ public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+ @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
+ throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
+ return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
+ true /* skipSigningBlock */);
+ }
+
+ @NonNull
+ private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
+ @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
+ boolean skipSigningBlock)
+ throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
+ long dataSize = apk.length();
+ if (skipSigningBlock) {
+ long signingBlockSize =
+ signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+ dataSize -= signingBlockSize;
+ }
int[] levelOffset = calculateVerityLevelOffset(dataSize);
int merkleTreeSize = levelOffset[levelOffset.length - 1];
ByteBuffer output = bufferFactory.create(
merkleTreeSize
- + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata
+ + CHUNK_SIZE_BYTES); // maximum size of apk-verity metadata
output.order(ByteOrder.LITTLE_ENDIAN);
-
ByteBuffer tree = slice(output, 0, merkleTreeSize);
- ByteBuffer header = slice(output, merkleTreeSize,
- merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
- ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
- merkleTreeSize + CHUNK_SIZE_BYTES);
- byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
- ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
- apkDigest.order(ByteOrder.LITTLE_ENDIAN);
+ // Only use default salt in legacy case.
+ byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
+ byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
+ tree, skipSigningBlock);
+ return new VerityResult(output, merkleTreeSize, apkRootHash);
+ }
- // NB: Buffer limit is set inside once finished.
- calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
-
- // Put the reverse offset to fs-verity header at the end.
- output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
- output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
- + 4); // size of this integer right before EOF
- output.flip();
-
- return new ApkVerityResult(output, apkDigestBytes);
+ static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
+ @NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput)
+ throws IOException {
+ footerOutput.order(ByteOrder.LITTLE_ENDIAN);
+ generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT);
+ long signingBlockSize =
+ signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+ generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset,
+ signingBlockSize, signatureInfo.eocdOffset);
}
/**
- * Calculates the fsverity root hash for integrity measurement. This needs to be consistent to
- * what kernel returns.
+ * Calculates the apk-verity root hash for integrity measurement. This needs to be consistent
+ * to what kernel returns.
*/
- static byte[] generateFsverityRootHash(RandomAccessFile apk, ByteBuffer apkDigest,
- SignatureInfo signatureInfo)
+ @NonNull
+ static byte[] generateApkVerityRootHash(@NonNull RandomAccessFile apk,
+ @NonNull ByteBuffer apkDigest, @NonNull SignatureInfo signatureInfo)
throws NoSuchAlgorithmException, DigestException, IOException {
- ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
- .order(ByteOrder.LITTLE_ENDIAN);
- ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
- ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
- CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
+ assertSigningBlockAlignedAndHasFullPages(signatureInfo);
- calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
+ ByteBuffer footer = ByteBuffer.allocate(CHUNK_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN);
+ generateApkVerityFooter(apk, signatureInfo, footer);
+ footer.flip();
MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
- md.update(header);
- md.update(extensions);
+ md.update(footer);
md.update(apkDigest);
return md.digest();
}
/**
- * Internal method to generate various parts of FSVerity constructs, including the header,
- * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the
- * generated data size and is readey for consuming.
+ * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This
+ * method does not check whether the root hash exists in the Signing Block or not.
+ *
+ * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+ * ByteBufferFactory}.
+ *
+ * @return the root hash of the generated hash tree.
*/
- private static void calculateFsveritySignatureInternal(
- RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
- ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
- throws IOException, NoSuchAlgorithmException, DigestException {
- assertSigningBlockAlignedAndHasFullPages(signatureInfo);
- long signingBlockSize =
- signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
- long dataSize = apk.length() - signingBlockSize;
- int[] levelOffset = calculateVerityLevelOffset(dataSize);
-
- if (treeOutput != null) {
- byte[] apkRootHash = generateApkVerityTree(apk, signatureInfo, DEFAULT_SALT,
- levelOffset, treeOutput);
- if (rootHashOutput != null) {
- rootHashOutput.put(apkRootHash);
- rootHashOutput.flip();
- }
- }
-
- if (headerOutput != null) {
- headerOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateFsverityHeader(headerOutput, apk.length(), levelOffset.length - 1,
- DEFAULT_SALT);
- }
-
- if (extensionsOutput != null) {
- extensionsOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateFsverityExtensions(extensionsOutput, signatureInfo.apkSigningBlockOffset,
- signingBlockSize, signatureInfo.eocdOffset);
+ @NonNull
+ static byte[] generateApkVerity(@NonNull String apkPath,
+ @NonNull ByteBufferFactory bufferFactory, @NonNull SignatureInfo signatureInfo)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
+ true /* skipSigningBlock */);
+ ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
+ result.verityData.limit());
+ generateApkVerityFooter(apk, signatureInfo, footer);
+ // Put the reverse offset to apk-verity header at the end.
+ footer.putInt(footer.position() + 4);
+ result.verityData.limit(result.merkleTreeSize + footer.position());
+ return result.rootHash;
}
}
@@ -180,11 +206,14 @@
private final byte[] mDigestBuffer = new byte[DIGEST_SIZE_BYTES];
private final byte[] mSalt;
- private BufferedDigester(byte[] salt, ByteBuffer output) throws NoSuchAlgorithmException {
+ private BufferedDigester(@Nullable byte[] salt, @NonNull ByteBuffer output)
+ throws NoSuchAlgorithmException {
mSalt = salt;
mOutput = output.slice();
mMd = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
- mMd.update(mSalt);
+ if (mSalt != null) {
+ mMd.update(mSalt);
+ }
mBytesDigestedSinceReset = 0;
}
@@ -211,7 +240,9 @@
mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
mOutput.put(mDigestBuffer);
// After digest, MessageDigest resets automatically, so no need to reset again.
- mMd.update(mSalt);
+ if (mSalt != null) {
+ mMd.update(mSalt);
+ }
mBytesDigestedSinceReset = 0;
}
}
@@ -252,6 +283,26 @@
// thus the syscall overhead is not too big.
private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024;
+ private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output)
+ throws IOException, NoSuchAlgorithmException, DigestException {
+ BufferedDigester digester = new BufferedDigester(null /* salt */, output);
+
+ // 1. Digest the whole file by chunks.
+ consumeByChunk(digester,
+ new MemoryMappedFileDataSource(file.getFD(), 0, file.length()),
+ MMAP_REGION_SIZE_BYTES);
+
+ // 2. Pad 0s up to the nearest 4096-byte block before hashing.
+ int lastIncompleteChunkSize = (int) (file.length() % CHUNK_SIZE_BYTES);
+ if (lastIncompleteChunkSize != 0) {
+ digester.consume(ByteBuffer.allocate(CHUNK_SIZE_BYTES - lastIncompleteChunkSize));
+ }
+ digester.assertEmptyBuffer();
+
+ // 3. Fill up the rest of buffer with 0s.
+ digester.fillUpLastOutputChunk();
+ }
+
private static void generateApkVerityDigestAtLeafLevel(RandomAccessFile apk,
SignatureInfo signatureInfo, byte[] salt, ByteBuffer output)
throws IOException, NoSuchAlgorithmException, DigestException {
@@ -297,12 +348,20 @@
digester.fillUpLastOutputChunk();
}
- private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
- byte[] salt, int[] levelOffset, ByteBuffer output)
+ @NonNull
+ private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
+ @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
+ @NonNull int[] levelOffset, @NonNull ByteBuffer output, boolean skipSigningBlock)
throws IOException, NoSuchAlgorithmException, DigestException {
// 1. Digest the apk to generate the leaf level hashes.
- generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
- levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
+ if (skipSigningBlock) {
+ assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+ generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
+ levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
+ } else {
+ generateFsVerityDigestAtLeafLevel(apk, slice(output,
+ levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
+ }
// 2. Digest the lower level hashes bottom up.
for (int level = levelOffset.length - 3; level >= 0; level--) {
@@ -324,7 +383,7 @@
return rootHash;
}
- private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth,
+ private static ByteBuffer generateApkVerityHeader(ByteBuffer buffer, long fileSize,
byte[] salt) {
if (salt.length != 8) {
throw new IllegalArgumentException("salt is not 8 bytes long");
@@ -351,13 +410,12 @@
buffer.put(salt); // salt (8 bytes)
skip(buffer, 22); // reserved
- buffer.flip();
return buffer;
}
- private static ByteBuffer generateFsverityExtensions(ByteBuffer buffer, long signingBlockOffset,
- long signingBlockSize, long eocdOffset) {
- // Snapshot of the FSVerity structs (subject to change once upstreamed).
+ private static ByteBuffer generateApkVerityExtensions(ByteBuffer buffer,
+ long signingBlockOffset, long signingBlockSize, long eocdOffset) {
+ // Snapshot of the experimental fs-verity structs (different from upstream).
//
// struct fsverity_extension_elide {
// __le64 offset;
@@ -409,7 +467,6 @@
skip(buffer, kPadding); // padding
}
- buffer.flip();
return buffer;
}
@@ -442,10 +499,11 @@
return levelOffset;
}
- private static void assertSigningBlockAlignedAndHasFullPages(SignatureInfo signatureInfo) {
+ private static void assertSigningBlockAlignedAndHasFullPages(
+ @NonNull SignatureInfo signatureInfo) {
if (signatureInfo.apkSigningBlockOffset % CHUNK_SIZE_BYTES != 0) {
throw new IllegalArgumentException(
- "APK Signing Block does not start at the page boundary: "
+ "APK Signing Block does not start at the page boundary: "
+ signatureInfo.apkSigningBlockOffset);
}
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 4b946d7..667fab5 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.graphics.BaseRecordingCanvas;
import android.graphics.Bitmap;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
@@ -35,7 +36,7 @@
*
* @hide
*/
-public final class DisplayListCanvas extends RecordingCanvas {
+public final class DisplayListCanvas extends BaseRecordingCanvas {
// The recording canvas pool should be large enough to handle a deeply nested
// view hierarchy because display lists are generated recursively.
private static final int POOL_LIMIT = 25;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bfe1e95..3bab87a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -17,8 +17,6 @@
package android.view;
import com.android.internal.os.IResultReceiver;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
@@ -71,8 +69,7 @@
boolean stopViewServer(); // Transaction #2
boolean isViewServerRunning(); // Transaction #3
- IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,
- in IInputContext inputContext);
+ IWindowSession openSession(in IWindowSessionCallback callback);
void getInitialDisplaySize(int displayId, out Point size);
void getBaseDisplaySize(int displayId, out Point size);
diff --git a/core/java/android/view/NativeVectorDrawableAnimator.java b/core/java/android/view/NativeVectorDrawableAnimator.java
new file mode 100644
index 0000000..b0556a3
--- /dev/null
+++ b/core/java/android/view/NativeVectorDrawableAnimator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * Exists just to allow for android.graphics & android.view package separation
+ *
+ * TODO: Get off of this coupling more cleanly somehow
+ *
+ * @hide
+ */
+public interface NativeVectorDrawableAnimator {
+ /** @hide */
+ long getAnimatorNativePtr();
+}
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index fcea0c3..8ae9127 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -24,7 +24,6 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -148,12 +147,12 @@
* @hide
*/
final long mNativeRenderNode;
- private final View mOwningView;
+ private final AnimationHost mAnimationHost;
- private RenderNode(String name, View owningView) {
+ private RenderNode(String name, AnimationHost animationHost) {
mNativeRenderNode = nCreate(name);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
- mOwningView = owningView;
+ mAnimationHost = animationHost;
}
/**
@@ -162,7 +161,7 @@
private RenderNode(long nativePtr) {
mNativeRenderNode = nativePtr;
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
- mOwningView = null;
+ mAnimationHost = null;
}
/**
@@ -174,8 +173,8 @@
* @return A new RenderNode.
*/
@UnsupportedAppUsage
- public static RenderNode create(String name, @Nullable View owningView) {
- return new RenderNode(name, owningView);
+ public static RenderNode create(String name, @Nullable AnimationHost animationHost) {
+ return new RenderNode(name, animationHost);
}
/**
@@ -189,10 +188,37 @@
}
/**
+ * Listens for RenderNode position updates for synchronous window movement.
+ *
+ * This is not suitable for generic position listening, it is only designed & intended
+ * for use by things which require external position events like SurfaceView, PopupWindow, etc..
+ *
+ * @hide
+ */
+ interface PositionUpdateListener {
+
+ /**
+ * Called by native by a Rendering Worker thread to update window position
+ *
+ * @hide
+ */
+ void positionChanged(long frameNumber, int left, int top, int right, int bottom);
+
+ /**
+ * Called by native on RenderThread to notify that the view is no longer in the
+ * draw tree. UI thread is blocked at this point.
+ *
+ * @hide
+ */
+ void positionLost(long frameNumber);
+
+ }
+
+ /**
* Enable callbacks for position changes.
*/
- public void requestPositionUpdates(SurfaceView view) {
- nRequestPositionUpdates(mNativeRenderNode, view);
+ public void requestPositionUpdates(PositionUpdateListener listener) {
+ nRequestPositionUpdates(mNativeRenderNode, listener);
}
@@ -843,30 +869,72 @@
return nGetDebugSize(mNativeRenderNode);
}
+ /**
+ * Sets whether or not to allow force dark to apply to this RenderNode.
+ *
+ * Setting this to false will disable the auto-dark feature on everything this RenderNode
+ * draws, including any descendants.
+ *
+ * Setting this to true will allow this RenderNode to be automatically made dark, however
+ * a value of 'true' will not override any 'false' value in its parent chain nor will
+ * it prevent any 'false' in any of its children.
+ *
+ * @param allow Whether or not to allow force dark.
+ * @return true If the value has changed, false otherwise.
+ */
+ public boolean setAllowForceDark(boolean allow) {
+ return nSetAllowForceDark(mNativeRenderNode, allow);
+ }
+
+ /**
+ * See {@link #setAllowForceDark(boolean)}
+ *
+ * @return true if force dark is allowed (default), false if it is disabled
+ */
+ public boolean getAllowForceDark() {
+ return nGetAllowForceDark(mNativeRenderNode);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Animations
///////////////////////////////////////////////////////////////////////////
+ /**
+ * TODO: Figure out if this can be eliminated/refactored away
+ *
+ * For now this interface exists to de-couple RenderNode from anything View-specific in a
+ * bit of a kludge.
+ *
+ * @hide */
+ interface AnimationHost {
+ void registerAnimatingRenderNode(RenderNode animator);
+ void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
+ boolean isAttached();
+ }
+
+ /** @hide */
public void addAnimator(RenderNodeAnimator animator) {
- if (mOwningView == null || mOwningView.mAttachInfo == null) {
+ if (!isAttached()) {
throw new IllegalStateException("Cannot start this animator on a detached view!");
}
nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
- mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
+ mAnimationHost.registerAnimatingRenderNode(this);
}
+ /** @hide */
public boolean isAttached() {
- return mOwningView != null && mOwningView.mAttachInfo != null;
+ return mAnimationHost != null && mAnimationHost.isAttached();
}
- public void registerVectorDrawableAnimator(
- AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) {
- if (mOwningView == null || mOwningView.mAttachInfo == null) {
+ /** @hide */
+ public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animatorSet) {
+ if (!isAttached()) {
throw new IllegalStateException("Cannot start this animator on a detached view!");
}
- mOwningView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animatorSet);
+ mAnimationHost.registerVectorDrawableAnimator(animatorSet);
}
+ /** @hide */
public void endAllAnimators() {
nEndAllAnimators(mNativeRenderNode);
}
@@ -880,7 +948,8 @@
private static native long nGetNativeFinalizer();
private static native void nOutput(long renderNode);
private static native int nGetDebugSize(long renderNode);
- private static native void nRequestPositionUpdates(long renderNode, SurfaceView callback);
+ private static native void nRequestPositionUpdates(long renderNode,
+ PositionUpdateListener callback);
// Animations
@@ -1043,4 +1112,8 @@
private static native int nGetWidth(long renderNode);
@CriticalNative
private static native int nGetHeight(long renderNode);
+ @CriticalNative
+ private static native boolean nSetAllowForceDark(long renderNode, boolean allowForceDark);
+ @CriticalNative
+ private static native boolean nGetAllowForceDark(long renderNode);
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 427f570..6fb1bba 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -895,6 +895,7 @@
HwuiContext(boolean isWideColorGamut) {
mRenderNode = RenderNode.create("HwuiCanvas", null);
mRenderNode.setClipToBounds(false);
+ mRenderNode.setAllowForceDark(false);
mIsWideColorGamut = isWideColorGamut;
mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject,
isWideColorGamut);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79eafa8..7271a9e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -70,15 +70,8 @@
private static native void nativeDestroy(long nativeObject);
private static native void nativeDisconnect(long nativeObject);
- private static native Bitmap nativeScreenshot(IBinder displayToken,
- Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean allLayers, boolean useIdentityTransform, int rotation);
- private static native GraphicBuffer nativeScreenshotToBuffer(IBinder displayToken,
- Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean allLayers, boolean useIdentityTransform, int rotation);
- private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
- Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean allLayers, boolean useIdentityTransform);
+ private static native GraphicBuffer nativeScreenshot(IBinder displayToken,
+ Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation);
private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
Rect sourceCrop, float frameScale);
@@ -1189,52 +1182,39 @@
}
/**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param display The display to take the screenshot of.
- * @param consumer The {@link Surface} to take the screenshot into.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param minLayer The lowest (bottom-most Z order) surface layer to
- * include in the screenshot.
- * @param maxLayer The highest (top-most Z order) surface layer to
- * include in the screenshot.
- * @param useIdentityTransform Replace whatever transformation (rotation,
- * scaling, translation) the surface layers are currently using with the
- * identity transformation while taking the screenshot.
- */
- public static void screenshot(IBinder display, Surface consumer,
- int width, int height, int minLayer, int maxLayer,
- boolean useIdentityTransform) {
- screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer,
- false, useIdentityTransform);
- }
-
- /**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param display The display to take the screenshot of.
- * @param consumer The {@link Surface} to take the screenshot into.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- */
- public static void screenshot(IBinder display, Surface consumer,
- int width, int height) {
- screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false);
- }
-
- /**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param display The display to take the screenshot of.
- * @param consumer The {@link Surface} to take the screenshot into.
+ * @see SurfaceControl#screenshot(IBinder, Surface, Rect, int, int, boolean, int)
*/
public static void screenshot(IBinder display, Surface consumer) {
- screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false);
+ screenshot(display, consumer, new Rect(), 0, 0, false, 0);
+ }
+
+ /**
+ * Copy the current screen contents into the provided {@link Surface}
+ *
+ * @param consumer The {@link Surface} to take the screenshot into.
+ * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)
+ */
+ public static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width,
+ int height, boolean useIdentityTransform, int rotation) {
+ if (consumer == null) {
+ throw new IllegalArgumentException("consumer must not be null");
+ }
+
+ final GraphicBuffer buffer = screenshotToBuffer(display, sourceCrop, width, height,
+ useIdentityTransform, rotation);
+ try {
+ consumer.attachAndQueueBuffer(buffer);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failed to take screenshot - " + e.getMessage());
+ }
+ }
+
+ /**
+ * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
+ */
+ @UnsupportedAppUsage
+ public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
+ return screenshot(sourceCrop, width, height, false, rotation);
}
/**
@@ -1242,79 +1222,16 @@
* Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
* a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
*
- * CAVEAT: Versions of screenshot that return a {@link Bitmap} can
- * be extremely slow; avoid use unless absolutely necessary; prefer
- * the versions that use a {@link Surface} instead, such as
- * {@link SurfaceControl#screenshot(IBinder, Surface)}.
+ * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
+ * unless absolutely necessary; prefer the versions that use a {@link Surface} such as
+ * {@link SurfaceControl#screenshot(IBinder, Surface)} or {@link GraphicBuffer} such as
+ * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
*
- * @param sourceCrop The portion of the screen to capture into the Bitmap;
- * caller may pass in 'new Rect()' if no cropping is desired.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param minLayer The lowest (bottom-most Z order) surface layer to
- * include in the screenshot.
- * @param maxLayer The highest (top-most Z order) surface layer to
- * include in the screenshot.
- * @param useIdentityTransform Replace whatever transformation (rotation,
- * scaling, translation) the surface layers are currently using with the
- * identity transformation while taking the screenshot.
- * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
- * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
- * screenshots in its native portrait orientation by default, so this is
- * useful for returning screenshots that are independent of device
- * orientation.
- * @return Returns a hardware Bitmap containing the screen contents, or null
- * if an error occurs. Make sure to call Bitmap.recycle() as soon as
- * possible, once its content is not needed anymore.
+ * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
*/
@UnsupportedAppUsage
public static Bitmap screenshot(Rect sourceCrop, int width, int height,
- int minLayer, int maxLayer, boolean useIdentityTransform,
- int rotation) {
- // TODO: should take the display as a parameter
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
- return nativeScreenshot(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, false, useIdentityTransform, rotation);
- }
-
- /**
- * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)}
- * but returns a GraphicBuffer.
- */
- public static GraphicBuffer screenshotToBuffer(Rect sourceCrop, int width, int height,
- int minLayer, int maxLayer, boolean useIdentityTransform,
- int rotation) {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
- return nativeScreenshotToBuffer(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, false, useIdentityTransform, rotation);
- }
-
- /**
- * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} but
- * includes all Surfaces in the screenshot. This will also update the orientation so it
- * sends the correct coordinates to SF based on the rotation value.
- *
- * @param sourceCrop The portion of the screen to capture into the Bitmap;
- * caller may pass in 'new Rect()' if no cropping is desired.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
- * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
- * screenshots in its native portrait orientation by default, so this is
- * useful for returning screenshots that are independent of device
- * orientation.
- * @return Returns a Bitmap containing the screen contents, or null
- * if an error occurs. Make sure to call Bitmap.recycle() as soon as
- * possible, once its content is not needed anymore.
- */
- @UnsupportedAppUsage
- public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
+ boolean useIdentityTransform, int rotation) {
// TODO: should take the display as a parameter
IBinder displayToken = SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
@@ -1323,22 +1240,45 @@
}
SurfaceControl.rotateCropForSF(sourceCrop, rotation);
- return nativeScreenshot(displayToken, sourceCrop, width, height, 0, 0, true,
- false, rotation);
+ final GraphicBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width, height,
+ useIdentityTransform, rotation);
+
+ if (buffer == null) {
+ Log.w(TAG, "Failed to take screenshot");
+ return null;
+ }
+ return Bitmap.createHardwareBitmap(buffer);
}
- @UnsupportedAppUsage
- private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop,
- int width, int height, int minLayer, int maxLayer, boolean allLayers,
- boolean useIdentityTransform) {
+ /**
+ * Captures all the surfaces in a display and returns a {@link GraphicBuffer} with the content.
+ *
+ * @param display The display to take the screenshot of.
+ * @param sourceCrop The portion of the screen to capture into the Bitmap; caller may
+ * pass in 'new Rect()' if no cropping is desired.
+ * @param width The desired width of the returned bitmap; the raw screen will be
+ * scaled down to this size; caller may pass in 0 if no scaling is
+ * desired.
+ * @param height The desired height of the returned bitmap; the raw screen will
+ * be scaled down to this size; caller may pass in 0 if no scaling
+ * is desired.
+ * @param useIdentityTransform Replace whatever transformation (rotation, scaling, translation)
+ * the surface layers are currently using with the identity
+ * transformation while taking the screenshot.
+ * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
+ * Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take
+ * screenshots in its native portrait orientation by default, so
+ * this is useful for returning screenshots that are independent of
+ * device orientation.
+ * @return Returns a GraphicBuffer that contains the captured content.
+ */
+ public static GraphicBuffer screenshotToBuffer(IBinder display, Rect sourceCrop, int width,
+ int height, boolean useIdentityTransform, int rotation) {
if (display == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- if (consumer == null) {
- throw new IllegalArgumentException("consumer must not be null");
- }
- nativeScreenshot(display, consumer, sourceCrop, width, height,
- minLayer, maxLayer, allLayers, useIdentityTransform);
+
+ return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation);
}
private static void rotateCropForSF(Rect crop, int rot) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 514a11e..e71182c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -209,7 +209,7 @@
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mRenderNode.requestPositionUpdates(this);
+ mRenderNode.requestPositionUpdates(mPositionListener);
setWillNotDraw(true);
}
@@ -826,81 +826,80 @@
private Rect mRTLastReportedPosition = new Rect();
- /**
- * Called by native by a Rendering Worker thread to update the window position
- * @hide
- */
- @UnsupportedAppUsage
- public final void updateSurfacePosition_renderWorker(long frameNumber,
- int left, int top, int right, int bottom) {
- if (mSurfaceControl == null) {
- return;
- }
+ private RenderNode.PositionUpdateListener mPositionListener =
+ new RenderNode.PositionUpdateListener() {
- // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
- // its 2nd frame if RenderThread is running slowly could potentially see
- // this as false, enter the branch, get pre-empted, then this comes along
- // and reports a new position, then the UI thread resumes and reports
- // its position. This could therefore be de-sync'd in that interval, but
- // the synchronization would violate the rule that RT must never block
- // on the UI thread which would open up potential deadlocks. The risk of
- // a single-frame desync is therefore preferable for now.
- mRtHandlingPositionUpdates = true;
- if (mRTLastReportedPosition.left == left
- && mRTLastReportedPosition.top == top
- && mRTLastReportedPosition.right == right
- && mRTLastReportedPosition.bottom == bottom) {
- return;
- }
- try {
- if (DEBUG) {
- Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
- "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
- frameNumber, left, top, right, bottom));
+ @Override
+ public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
+ if (mSurfaceControl == null) {
+ return;
}
- mRTLastReportedPosition.set(left, top, right, bottom);
- setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
- // Now overwrite mRTLastReportedPosition with our values
- } catch (Exception ex) {
- Log.e(TAG, "Exception from repositionChild", ex);
- }
- }
- /**
- * Called by native on RenderThread to notify that the view is no longer in the
- * draw tree. UI thread is blocked at this point.
- * @hide
- */
- @UnsupportedAppUsage
- public final void surfacePositionLost_uiRtSync(long frameNumber) {
- if (DEBUG) {
- Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
- System.identityHashCode(this), frameNumber));
+ // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
+ // its 2nd frame if RenderThread is running slowly could potentially see
+ // this as false, enter the branch, get pre-empted, then this comes along
+ // and reports a new position, then the UI thread resumes and reports
+ // its position. This could therefore be de-sync'd in that interval, but
+ // the synchronization would violate the rule that RT must never block
+ // on the UI thread which would open up potential deadlocks. The risk of
+ // a single-frame desync is therefore preferable for now.
+ mRtHandlingPositionUpdates = true;
+ if (mRTLastReportedPosition.left == left
+ && mRTLastReportedPosition.top == top
+ && mRTLastReportedPosition.right == right
+ && mRTLastReportedPosition.bottom == bottom) {
+ return;
+ }
+ try {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "%d updateSurfacePosition RenderWorker, frameNr = %d, "
+ + "postion = [%d, %d, %d, %d]",
+ System.identityHashCode(this), frameNumber,
+ left, top, right, bottom));
+ }
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+ // Now overwrite mRTLastReportedPosition with our values
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception from repositionChild", ex);
+ }
}
- mRTLastReportedPosition.setEmpty();
- if (mSurfaceControl == null) {
- return;
- }
- if (mRtHandlingPositionUpdates) {
- mRtHandlingPositionUpdates = false;
- // This callback will happen while the UI thread is blocked, so we can
- // safely access other member variables at this time.
- // So do what the UI thread would have done if RT wasn't handling position
- // updates.
- if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
- try {
- if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
- "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
- mScreenRect.left, mScreenRect.top,
- mScreenRect.right, mScreenRect.bottom));
- setParentSpaceRectangle(mScreenRect, frameNumber);
- } catch (Exception ex) {
- Log.e(TAG, "Exception configuring surface", ex);
+ @Override
+ public void positionLost(long frameNumber) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
+ System.identityHashCode(this), frameNumber));
+ }
+ mRTLastReportedPosition.setEmpty();
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+ if (mRtHandlingPositionUpdates) {
+ mRtHandlingPositionUpdates = false;
+ // This callback will happen while the UI thread is blocked, so we can
+ // safely access other member variables at this time.
+ // So do what the UI thread would have done if RT wasn't handling position
+ // updates.
+ if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%d updateSurfacePosition, "
+ + "postion = [%d, %d, %d, %d]",
+ System.identityHashCode(this),
+ mScreenRect.left, mScreenRect.top,
+ mScreenRect.right, mScreenRect.bottom));
+ }
+ setParentSpaceRectangle(mScreenRect, frameNumber);
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
}
}
}
- }
+ };
private SurfaceHolder.Callback[] getSurfaceCallbacks() {
SurfaceHolder.Callback callbacks[];
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 6737839..42690ce 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -25,7 +25,6 @@
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -914,8 +913,7 @@
nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
}
- void registerVectorDrawableAnimator(
- AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
+ void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode,
animator.getAnimatorNativePtr());
}
@@ -976,6 +974,25 @@
}
}
+ /** The root of everything */
+ public @NonNull RenderNode getRootNode() {
+ return mRootNode;
+ }
+
+ private boolean mForceDark = false;
+
+ /**
+ * Whether or not the force-dark feature should be used for this renderer.
+ */
+ public boolean setForceDark(boolean enable) {
+ if (mForceDark != enable) {
+ mForceDark = enable;
+ nSetForceDark(mNativeProxy, enable);
+ return true;
+ }
+ return false;
+ }
+
/**
* Basic synchronous renderer. Currently only used to render the Magnifier, so use with care.
* TODO: deduplicate against ThreadedRenderer.
@@ -1255,4 +1272,5 @@
private static native void nSetIsolatedProcess(boolean enabled);
private static native void nSetContextPriority(int priority);
private static native void nAllocateBuffers(long nativeProxy, Surface window);
+ private static native void nSetForceDark(long nativeProxy, boolean enabled);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 19e3f7f62..b2944d6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4864,7 +4864,7 @@
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
- mRenderNode = RenderNode.create(getClass().getName(), this);
+ mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this));
if (!sCompatibilityDone && context != null) {
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
@@ -5732,7 +5732,7 @@
@UnsupportedAppUsage
View() {
mResources = null;
- mRenderNode = RenderNode.create(getClass().getName(), this);
+ mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this));
}
final boolean debugDraw() {
@@ -15246,6 +15246,44 @@
}
/**
+ * Sets whether or not to allow force dark to apply to this view.
+ *
+ * Setting this to false will disable the auto-dark feature on everything this view
+ * draws, including any descendants.
+ *
+ * Setting this to true will allow this view to be automatically made dark, however
+ * a value of 'true' will not override any 'false' value in its parent chain nor will
+ * it prevent any 'false' in any of its children.
+ *
+ * The default behavior of force dark is also influenced by the Theme's
+ * {@link android.R.styleable#Theme_isLightTheme isLightTheme} attribute.
+ * If a theme is isLightTheme="false", then force dark is globally disabled for that theme.
+ *
+ * @param allow Whether or not to allow force dark.
+ *
+ * @hide
+ */
+ public void setAllowForceDark(boolean allow) {
+ if (mRenderNode.setAllowForceDark(allow)) {
+ // Currently toggling force-dark requires a new display list push to apply
+ // TODO: Make it not clobber the display list so this is just a damageSelf() instead
+ invalidate();
+ }
+ }
+
+ /**
+ * See {@link #setAllowForceDark(boolean)}
+ *
+ * @return true if force dark is allowed (default), false if it is disabled
+ *
+ * @hide
+ */
+ @ViewDebug.ExportedProperty(category = "drawing")
+ public boolean getAllowForceDark() {
+ return mRenderNode.getAllowForceDark();
+ }
+
+ /**
* Top position of this view relative to its parent.
*
* @return The top of this view, in pixels.
@@ -15598,7 +15636,7 @@
/**
* Sets the visual z position of this view, in pixels. This is equivalent to setting the
* {@link #setTranslationZ(float) translationZ} property to be the difference between
- * the x value passed in and the current {@link #getElevation() elevation} property.
+ * the z value passed in and the current {@link #getElevation() elevation} property.
*
* @param z The visual z position of this view, in pixels.
*/
@@ -20566,7 +20604,8 @@
*/
private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
if (renderNode == null) {
- renderNode = RenderNode.create(drawable.getClass().getName(), this);
+ renderNode = RenderNode.create(drawable.getClass().getName(),
+ new ViewAnimationHostBridge(this));
renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND);
}
diff --git a/core/java/android/view/ViewAnimationHostBridge.java b/core/java/android/view/ViewAnimationHostBridge.java
new file mode 100644
index 0000000..58f555d
--- /dev/null
+++ b/core/java/android/view/ViewAnimationHostBridge.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * Maps a View to a RenderNode's AnimationHost
+ *
+ * @hide
+ */
+public class ViewAnimationHostBridge implements RenderNode.AnimationHost {
+ private final View mView;
+
+ /**
+ * @param view the View to bridge to an AnimationHost
+ */
+ public ViewAnimationHostBridge(View view) {
+ mView = view;
+ }
+
+ @Override
+ public void registerAnimatingRenderNode(RenderNode animator) {
+ mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(animator);
+ }
+
+ @Override
+ public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
+ mView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animator);
+ }
+
+ @Override
+ public boolean isAttached() {
+ return mView.mAttachInfo != null;
+ }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2ee7ab9..bef8e8f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -43,6 +43,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
@@ -52,7 +53,6 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -239,6 +239,12 @@
final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
@UnsupportedAppUsage
final Context mContext;
+ /**
+ * TODO(b/116349163): Check if we can merge this into {@link #mContext}.
+ */
+ @NonNull
+ private Context mDisplayContext;
+
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -532,6 +538,7 @@
public ViewRootImpl(Context context, Display display) {
mContext = context;
+ mDisplayContext = context.createDisplayContext(display);
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
@@ -984,6 +991,9 @@
ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
}
+ /**
+ * @param animator animator to register with the hardware renderer
+ */
public void registerAnimatingRenderNode(RenderNode animator) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.registerAnimatingRenderNode(animator);
@@ -995,8 +1005,10 @@
}
}
- public void registerVectorDrawableAnimator(
- AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
+ /**
+ * @param animator animator to register with the hardware renderer
+ */
+ public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.registerVectorDrawableAnimator(animator);
}
@@ -1066,6 +1078,7 @@
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut);
+ updateForceDarkMode();
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mHardwareAccelerated =
mAttachInfo.mHardwareAccelerationRequested = true;
@@ -1074,6 +1087,27 @@
}
}
+ private int getNightMode() {
+ return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ }
+
+ private void updateForceDarkMode() {
+ if (mAttachInfo.mThreadedRenderer == null) return;
+
+ boolean nightMode = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
+ TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
+ boolean isLightTheme = a.getBoolean(R.styleable.Theme_isLightTheme, false);
+ a.recycle();
+
+ boolean changed = mAttachInfo.mThreadedRenderer.setForceDark(nightMode);
+ changed |= mAttachInfo.mThreadedRenderer.getRootNode().setAllowForceDark(isLightTheme);
+
+ if (changed) {
+ // TODO: Don't require regenerating all display lists to apply this setting
+ invalidateWorld(mView);
+ }
+ }
+
@UnsupportedAppUsage
public View getView() {
return mView;
@@ -1249,6 +1283,7 @@
} else {
mDisplay = preferredDisplay;
}
+ mDisplayContext = mContext.createDisplayContext(mDisplay);
}
void pokeDrawLockIfNeeded() {
@@ -2579,7 +2614,7 @@
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2695,7 +2730,7 @@
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPreWindowFocus(mView, hasWindowFocus);
}
@@ -4065,6 +4100,8 @@
mForceNextWindowRelayout = true;
requestLayout();
}
+
+ updateForceDarkMode();
}
/**
@@ -4329,7 +4366,8 @@
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm =
+ mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.checkFocus();
}
@@ -4871,7 +4909,7 @@
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index e20acf1..46aea80 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -196,7 +196,10 @@
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
- InputMethodManager imm = InputMethodManager.getInstance();
+ // Emulate the legacy behavior. The global instance of InputMethodManager
+ // was instantiated here.
+ // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
+ InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@@ -204,8 +207,7 @@
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
- },
- imm.getClient(), imm.getInputContext());
+ });
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 87b7b05..0e1f767 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -203,11 +203,6 @@
*/
private float mScaleFactor = 1f;
- /**
- * Don't animate the wallpaper.
- */
- private boolean mDetachWallpaper = false;
-
private boolean mShowWallpaper;
private boolean mMore = true;
@@ -667,9 +662,10 @@
*
* @param detachWallpaper true if the wallpaper should be detached from the animation
* @attr ref android.R.styleable#Animation_detachWallpaper
+ *
+ * @deprecated All window animations are running with detached wallpaper.
*/
public void setDetachWallpaper(boolean detachWallpaper) {
- mDetachWallpaper = detachWallpaper;
}
/**
@@ -793,9 +789,11 @@
/**
* Return value of {@link #setDetachWallpaper(boolean)}.
* @attr ref android.R.styleable#Animation_detachWallpaper
+ *
+ * @deprecated All window animations are running with detached wallpaper.
*/
public boolean getDetachWallpaper() {
- return mDetachWallpaper;
+ return false;
}
/**
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index f89743c..2d1249d 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -41,8 +41,8 @@
* Constructor
*
* @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
- * an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
- * ease-out effect (i.e., it starts even faster and ends evens slower)
+ * an upside-down y=x^2 parabola. Increasing factor above 1.0f exaggerates the
+ * ease-out effect (i.e., it starts even faster and ends evens slower).
*/
public DecelerateInterpolator(float factor) {
mFactor = factor;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e87048e..7abe19e79 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -301,6 +301,14 @@
*/
public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
+ /**
+ * Same as {@link #STATE_UNKNOWN}, but used on
+ * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
+ * the service failed to fullfil a request.
+ *
+ * @hide
+ */
+ public static final int STATE_UNKNOWN_FAILED = 6;
/**
* Timeout in ms for calls to the field classification service.
@@ -2023,8 +2031,10 @@
* @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
* FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
* {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
- * changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
- * disabled further autofill requests for the activity).
+ * changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
+ * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
+ * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill
+ * service disabled further autofill requests for the activity).
*/
private void setSessionFinished(int newState) {
synchronized (mLock) {
@@ -2032,7 +2042,7 @@
Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
+ getStateAsString(newState));
}
- if (newState == STATE_UNKNOWN_COMPAT_MODE) {
+ if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
resetSessionLocked(/* resetEnteredIds= */ true);
mState = STATE_UNKNOWN;
} else {
@@ -2229,6 +2239,8 @@
return "DISABLED_BY_SERVICE";
case STATE_UNKNOWN_COMPAT_MODE:
return "UNKNOWN_COMPAT_MODE";
+ case STATE_UNKNOWN_FAILED:
+ return "UNKNOWN_FAILED";
default:
return "INVALID:" + state;
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index e1600c4..d09323d 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -124,7 +124,8 @@
* @hide
*/
@MainThread
- public void updateInputMethodDisplay(int displayId);
+ default void updateInputMethodDisplay(int displayId) {
+ }
/**
* Bind a new application environment in to the input method, so that it
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 2f677f9..ca2ccaf 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -57,6 +57,7 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
@@ -69,6 +70,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -225,6 +227,54 @@
static final String PENDING_EVENT_COUNTER = "aq:imm";
+ /**
+ * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
+ * or indirectly relied on {@link #sInstance} via reflection or something like that.
+ *
+ * <p>Here are scenarios we know and there could be more scenarios we are not
+ * aware of right know.</p>
+ *
+ * <ul>
+ * <li>Apps that directly access {@link #sInstance} via reflection, which is currently
+ * allowed because of {@link UnsupportedAppUsage} annotation. Currently
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+ * {@link #sInstance} is not {@code null} when such an app is accessing it, but removing
+ * that code from {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal
+ * untested code paths in their apps, which probably happen in an early startup time of that
+ * app.</li>
+ * <li>Apps that directly access {@link #peekInstance()} via reflection, which is currently
+ * allowed because of {@link UnsupportedAppUsage} annotation. Currently
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+ * {@link #peekInstance()} returns non-{@code null} object when such an app is calling
+ * {@link #peekInstance()}, but removing that code from
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal untested code
+ * paths in their apps, which probably happen in an early startup time of that app. The good
+ * news is that unlike {@link #sInstance}'s case we can at least work around this scenario
+ * by changing the semantics of {@link #peekInstance()}, which is currently defined as
+ * "retrieve the global {@link InputMethodManager} instance, if it exists" to something that
+ * always returns non-{@code null} {@link InputMethodManager}. However, introducing such an
+ * workaround can also trigger different compatibility issues if {@link #peekInstance()} was
+ * called before {@link android.view.WindowManagerGlobal#getWindowSession()} and it expected
+ * {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li>
+ * </ul>
+ *
+ * <p>Since this is purely a compatibility hack, this method must be used only from
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} and {@link #getInstance()}.</p>
+ *
+ * <p>TODO(Bug 116157766): Remove this method once we clean up {@link UnsupportedAppUsage}.</p>
+ * @hide
+ */
+ public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() {
+ getInstanceInternal();
+ }
+
+ private static final Object sLock = new Object();
+
+ /**
+ * @deprecated This cannot be compatible with multi-display. Please do not use this.
+ */
+ @Deprecated
+ @GuardedBy("sLock")
@UnsupportedAppUsage
static InputMethodManager sInstance;
@@ -628,13 +678,63 @@
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
- InputMethodManager(Looper looper) throws ServiceNotFoundException {
- this(IInputMethodManager.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
+ /**
+ * For layoutlib to clean up static objects inside {@link InputMethodManager}.
+ */
+ static void tearDownEditMode() {
+ if (!isInEditMode()) {
+ throw new UnsupportedOperationException(
+ "This method must be called only from layoutlib");
+ }
+ synchronized (sLock) {
+ sInstance = null;
+ }
}
- InputMethodManager(IInputMethodManager service, Looper looper) {
- mService = service;
+ /**
+ * For layoutlib to override this method to return {@code true}.
+ *
+ * @return {@code true} if the process is running for developer tools
+ * @see View#isInEditMode()
+ */
+ private static boolean isInEditMode() {
+ return false;
+ }
+
+ private static IInputMethodManager getIInputMethodManager() throws ServiceNotFoundException {
+ if (!isInEditMode()) {
+ return IInputMethodManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+ }
+ // If InputMethodManager is running for layoutlib, stub out IPCs into IMMS.
+ final Class<IInputMethodManager> c = IInputMethodManager.class;
+ return (IInputMethodManager) Proxy.newProxyInstance(c.getClassLoader(),
+ new Class[]{c}, (proxy, method, args) -> {
+ final Class<?> returnType = method.getReturnType();
+ if (returnType == boolean.class) {
+ return false;
+ } else if (returnType == int.class) {
+ return 0;
+ } else if (returnType == long.class) {
+ return 0L;
+ } else if (returnType == short.class) {
+ return 0;
+ } else if (returnType == char.class) {
+ return 0;
+ } else if (returnType == byte.class) {
+ return 0;
+ } else if (returnType == float.class) {
+ return 0f;
+ } else if (returnType == double.class) {
+ return 0.0;
+ } else {
+ return null;
+ }
+ });
+ }
+
+ InputMethodManager(Looper looper) throws ServiceNotFoundException {
+ mService = getIInputMethodManager();
mMainLooper = looper;
mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper,
@@ -642,17 +742,20 @@
}
/**
- * Retrieve the global InputMethodManager instance, creating it if it
- * doesn't already exist.
+ * Retrieve the global {@link InputMethodManager} instance, creating it if it doesn't already
+ * exist.
+ *
+ * @return global {@link InputMethodManager} instance
* @hide
*/
- @UnsupportedAppUsage
- public static InputMethodManager getInstance() {
- synchronized (InputMethodManager.class) {
+ public static InputMethodManager getInstanceInternal() {
+ synchronized (sLock) {
if (sInstance == null) {
try {
- sInstance = new InputMethodManager(Looper.getMainLooper());
- } catch (ServiceNotFoundException e) {
+ final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper());
+ imm.mService.addClient(imm.mClient, imm.mIInputContext);
+ sInstance = imm;
+ } catch (ServiceNotFoundException | RemoteException e) {
throw new IllegalStateException(e);
}
}
@@ -661,15 +764,42 @@
}
/**
- * Private optimization: retrieve the global InputMethodManager instance, if it exists.
- * @hide
+ * Deprecated. Do not use.
+ *
+ * @return global {@link InputMethodManager} instance
* @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
* support multi-display scenario.
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static InputMethodManager getInstance() {
+ Log.w(TAG, "InputMethodManager.getInstance() is deprecated because it cannot be"
+ + " compatible with multi-display."
+ + " Use context.getSystemService(InputMethodManager.class) instead.",
+ new Throwable());
+ ensureDefaultInstanceForDefaultDisplayIfNecessary();
+ return peekInstance();
+ }
+
+ /**
+ * Deprecated. Do not use.
+ *
+ * @return {@link #sInstance}
+ * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
+ * support multi-display scenario.
+ * @hide
*/
@Deprecated
@UnsupportedAppUsage
public static InputMethodManager peekInstance() {
- return sInstance;
+ Log.w(TAG, "InputMethodManager.peekInstance() is deprecated because it cannot be"
+ + " compatible with multi-display."
+ + " Use context.getSystemService(InputMethodManager.class) instead.",
+ new Throwable());
+ synchronized (sLock) {
+ return sInstance;
+ }
}
/** @hide */
@@ -1348,6 +1478,10 @@
mServedView.getWindowToken() == appWindowToken) {
finishInputLocked();
}
+ if (mCurRootView != null &&
+ mCurRootView.getWindowToken() == appWindowToken) {
+ mCurRootView = null;
+ }
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 29339e9..3e240cf 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -44,6 +44,8 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.google.android.textclassifier.AnnotatorModel;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -92,7 +94,7 @@
@GuardedBy("mLock") // Do not access outside this lock.
private ModelFile mModel;
@GuardedBy("mLock") // Do not access outside this lock.
- private TextClassifierImplNative mNative;
+ private AnnotatorModel mNative;
private final Object mLoggerLock = new Object();
@GuardedBy("mLoggerLock") // Do not access outside this lock.
@@ -125,7 +127,7 @@
&& rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
final String localesString = concatenateLocales(request.getDefaultLocales());
final ZonedDateTime refTime = ZonedDateTime.now();
- final TextClassifierImplNative nativeImpl = getNative(request.getDefaultLocales());
+ final AnnotatorModel nativeImpl = getNative(request.getDefaultLocales());
final int start;
final int end;
if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
@@ -134,7 +136,7 @@
} else {
final int[] startEnd = nativeImpl.suggestSelection(
string, request.getStartIndex(), request.getEndIndex(),
- new TextClassifierImplNative.SelectionOptions(localesString));
+ new AnnotatorModel.SelectionOptions(localesString));
start = startEnd[0];
end = startEnd[1];
}
@@ -142,10 +144,10 @@
&& start >= 0 && end <= string.length()
&& start <= request.getStartIndex() && end >= request.getEndIndex()) {
final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
- final TextClassifierImplNative.ClassificationResult[] results =
+ final AnnotatorModel.ClassificationResult[] results =
nativeImpl.classifyText(
string, start, end,
- new TextClassifierImplNative.ClassificationOptions(
+ new AnnotatorModel.ClassificationOptions(
refTime.toInstant().toEpochMilli(),
refTime.getZone().getId(),
localesString));
@@ -184,11 +186,11 @@
final String localesString = concatenateLocales(request.getDefaultLocales());
final ZonedDateTime refTime = request.getReferenceTime() != null
? request.getReferenceTime() : ZonedDateTime.now();
- final TextClassifierImplNative.ClassificationResult[] results =
+ final AnnotatorModel.ClassificationResult[] results =
getNative(request.getDefaultLocales())
.classifyText(
string, request.getStartIndex(), request.getEndIndex(),
- new TextClassifierImplNative.ClassificationOptions(
+ new AnnotatorModel.ClassificationOptions(
refTime.toInstant().toEpochMilli(),
refTime.getZone().getId(),
localesString));
@@ -228,17 +230,17 @@
? request.getEntityConfig().resolveEntityListModifications(
getEntitiesForHints(request.getEntityConfig().getHints()))
: mSettings.getEntityListDefault();
- final TextClassifierImplNative nativeImpl =
+ final AnnotatorModel nativeImpl =
getNative(request.getDefaultLocales());
- final TextClassifierImplNative.AnnotatedSpan[] annotations =
+ final AnnotatorModel.AnnotatedSpan[] annotations =
nativeImpl.annotate(
textString,
- new TextClassifierImplNative.AnnotationOptions(
+ new AnnotatorModel.AnnotationOptions(
refTime.toInstant().toEpochMilli(),
refTime.getZone().getId(),
concatenateLocales(request.getDefaultLocales())));
- for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
- final TextClassifierImplNative.ClassificationResult[] results =
+ for (AnnotatorModel.AnnotatedSpan span : annotations) {
+ final AnnotatorModel.ClassificationResult[] results =
span.getClassification();
if (results.length == 0
|| !entitiesToIdentify.contains(results[0].getCollection())) {
@@ -297,7 +299,7 @@
}
}
- private TextClassifierImplNative getNative(LocaleList localeList)
+ private AnnotatorModel getNative(LocaleList localeList)
throws FileNotFoundException {
synchronized (mLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
@@ -310,7 +312,7 @@
destroyNativeIfExistsLocked();
final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- mNative = new TextClassifierImplNative(fd.getFd());
+ mNative = new AnnotatorModel(fd.getFd());
closeAndLogError(fd);
mModel = bestModel;
}
@@ -398,14 +400,14 @@
}
private TextClassification createClassificationResult(
- TextClassifierImplNative.ClassificationResult[] classifications,
+ AnnotatorModel.ClassificationResult[] classifications,
String text, int start, int end, @Nullable Instant referenceTime) {
final String classifiedText = text.substring(start, end);
final TextClassification.Builder builder = new TextClassification.Builder()
.setText(classifiedText);
final int size = classifications.length;
- TextClassifierImplNative.ClassificationResult highestScoringResult = null;
+ AnnotatorModel.ClassificationResult highestScoringResult = null;
float highestScore = Float.MIN_VALUE;
for (int i = 0; i < size; i++) {
builder.setEntityType(classifications[i].getCollection(),
@@ -486,9 +488,9 @@
try {
final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
file, ParcelFileDescriptor.MODE_READ_ONLY);
- final int version = TextClassifierImplNative.getVersion(modelFd.getFd());
+ final int version = AnnotatorModel.getVersion(modelFd.getFd());
final String supportedLocalesStr =
- TextClassifierImplNative.getLocales(modelFd.getFd());
+ AnnotatorModel.getLocales(modelFd.getFd());
if (supportedLocalesStr.isEmpty()) {
Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
return null;
@@ -676,7 +678,7 @@
public static List<LabeledIntent> create(
Context context,
@Nullable Instant referenceTime,
- TextClassifierImplNative.ClassificationResult classification,
+ AnnotatorModel.ClassificationResult classification,
String text) {
final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
text = text.trim();
diff --git a/core/java/android/view/textclassifier/TextClassifierImplNative.java b/core/java/android/view/textclassifier/TextClassifierImplNative.java
deleted file mode 100644
index 3d4c8f2..0000000
--- a/core/java/android/view/textclassifier/TextClassifierImplNative.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.content.res.AssetFileDescriptor;
-
-/**
- * Java wrapper for TextClassifier native library interface. This library is used for detecting
- * entities in text.
- */
-final class TextClassifierImplNative {
-
- static {
- System.loadLibrary("textclassifier");
- }
-
- private final long mModelPtr;
-
- /**
- * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
- * a file descriptor.
- */
- TextClassifierImplNative(int fd) {
- mModelPtr = nativeNew(fd);
- if (mModelPtr == 0L) {
- throw new IllegalArgumentException("Couldn't initialize TC from file descriptor.");
- }
- }
-
- /**
- * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
- * a file path.
- */
- TextClassifierImplNative(String path) {
- mModelPtr = nativeNewFromPath(path);
- if (mModelPtr == 0L) {
- throw new IllegalArgumentException("Couldn't initialize TC from given file.");
- }
- }
-
- /**
- * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
- * an AssetFileDescriptor.
- */
- TextClassifierImplNative(AssetFileDescriptor afd) {
- mModelPtr = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength());
- if (mModelPtr == 0L) {
- throw new IllegalArgumentException(
- "Couldn't initialize TC from given AssetFileDescriptor");
- }
- }
-
- /**
- * Given a string context and current selection, computes the SmartSelection suggestion.
- *
- * <p>The begin and end are character indices into the context UTF8 string. selectionBegin is
- * the character index where the selection begins, and selectionEnd is the index of one
- * character past the selection span.
- *
- * <p>The return value is an array of two ints: suggested selection beginning and end, with the
- * same semantics as the input selectionBeginning and selectionEnd.
- */
- public int[] suggestSelection(
- String context, int selectionBegin, int selectionEnd, SelectionOptions options) {
- return nativeSuggestSelection(mModelPtr, context, selectionBegin, selectionEnd, options);
- }
-
- /**
- * Given a string context and current selection, classifies the type of the selected text.
- *
- * <p>The begin and end params are character indices in the context string.
- *
- * <p>Returns an array of ClassificationResult objects with the probability scores for different
- * collections.
- */
- public ClassificationResult[] classifyText(
- String context, int selectionBegin, int selectionEnd, ClassificationOptions options) {
- return nativeClassifyText(mModelPtr, context, selectionBegin, selectionEnd, options);
- }
-
- /**
- * Annotates given input text. The annotations should cover the whole input context except for
- * whitespaces, and are sorted by their position in the context string.
- */
- public AnnotatedSpan[] annotate(String text, AnnotationOptions options) {
- return nativeAnnotate(mModelPtr, text, options);
- }
-
- /** Frees up the allocated memory. */
- public void close() {
- nativeClose(mModelPtr);
- }
-
- /** Returns a comma separated list of locales supported by the model as BCP 47 tags. */
- public static String getLocales(int fd) {
- return nativeGetLocales(fd);
- }
-
- /** Returns the version of the model. */
- public static int getVersion(int fd) {
- return nativeGetVersion(fd);
- }
-
- /** Represents a datetime parsing result from classifyText calls. */
- public static final class DatetimeResult {
- static final int GRANULARITY_YEAR = 0;
- static final int GRANULARITY_MONTH = 1;
- static final int GRANULARITY_WEEK = 2;
- static final int GRANULARITY_DAY = 3;
- static final int GRANULARITY_HOUR = 4;
- static final int GRANULARITY_MINUTE = 5;
- static final int GRANULARITY_SECOND = 6;
-
- private final long mTimeMsUtc;
- private final int mGranularity;
-
- DatetimeResult(long timeMsUtc, int granularity) {
- mGranularity = granularity;
- mTimeMsUtc = timeMsUtc;
- }
-
- public long getTimeMsUtc() {
- return mTimeMsUtc;
- }
-
- public int getGranularity() {
- return mGranularity;
- }
- }
-
- /** Represents a result of classifyText method call. */
- public static final class ClassificationResult {
- private final String mCollection;
- private final float mScore;
- private final DatetimeResult mDatetimeResult;
-
- ClassificationResult(
- String collection, float score, DatetimeResult datetimeResult) {
- mCollection = collection;
- mScore = score;
- mDatetimeResult = datetimeResult;
- }
-
- public String getCollection() {
- if (mCollection.equals(TextClassifier.TYPE_DATE) && mDatetimeResult != null) {
- switch (mDatetimeResult.getGranularity()) {
- case DatetimeResult.GRANULARITY_HOUR:
- // fall through
- case DatetimeResult.GRANULARITY_MINUTE:
- // fall through
- case DatetimeResult.GRANULARITY_SECOND:
- return TextClassifier.TYPE_DATE_TIME;
- default:
- return TextClassifier.TYPE_DATE;
- }
- }
- return mCollection;
- }
-
- public float getScore() {
- return mScore;
- }
-
- public DatetimeResult getDatetimeResult() {
- return mDatetimeResult;
- }
- }
-
- /** Represents a result of Annotate call. */
- public static final class AnnotatedSpan {
- private final int mStartIndex;
- private final int mEndIndex;
- private final ClassificationResult[] mClassification;
-
- AnnotatedSpan(
- int startIndex, int endIndex, ClassificationResult[] classification) {
- mStartIndex = startIndex;
- mEndIndex = endIndex;
- mClassification = classification;
- }
-
- public int getStartIndex() {
- return mStartIndex;
- }
-
- public int getEndIndex() {
- return mEndIndex;
- }
-
- public ClassificationResult[] getClassification() {
- return mClassification;
- }
- }
-
- /** Represents options for the suggestSelection call. */
- public static final class SelectionOptions {
- private final String mLocales;
-
- SelectionOptions(String locales) {
- mLocales = locales;
- }
-
- public String getLocales() {
- return mLocales;
- }
- }
-
- /** Represents options for the classifyText call. */
- public static final class ClassificationOptions {
- private final long mReferenceTimeMsUtc;
- private final String mReferenceTimezone;
- private final String mLocales;
-
- ClassificationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) {
- mReferenceTimeMsUtc = referenceTimeMsUtc;
- mReferenceTimezone = referenceTimezone;
- mLocales = locale;
- }
-
- public long getReferenceTimeMsUtc() {
- return mReferenceTimeMsUtc;
- }
-
- public String getReferenceTimezone() {
- return mReferenceTimezone;
- }
-
- public String getLocale() {
- return mLocales;
- }
- }
-
- /** Represents options for the Annotate call. */
- public static final class AnnotationOptions {
- private final long mReferenceTimeMsUtc;
- private final String mReferenceTimezone;
- private final String mLocales;
-
- AnnotationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) {
- mReferenceTimeMsUtc = referenceTimeMsUtc;
- mReferenceTimezone = referenceTimezone;
- mLocales = locale;
- }
-
- public long getReferenceTimeMsUtc() {
- return mReferenceTimeMsUtc;
- }
-
- public String getReferenceTimezone() {
- return mReferenceTimezone;
- }
-
- public String getLocale() {
- return mLocales;
- }
- }
-
- private static native long nativeNew(int fd);
-
- private static native long nativeNewFromPath(String path);
-
- private static native long nativeNewFromAssetFileDescriptor(
- AssetFileDescriptor afd, long offset, long size);
-
- private static native int[] nativeSuggestSelection(
- long context,
- String text,
- int selectionBegin,
- int selectionEnd,
- SelectionOptions options);
-
- private static native ClassificationResult[] nativeClassifyText(
- long context,
- String text,
- int selectionBegin,
- int selectionEnd,
- ClassificationOptions options);
-
- private static native AnnotatedSpan[] nativeAnnotate(
- long context, String text, AnnotationOptions options);
-
- private static native void nativeClose(long context);
-
- private static native String nativeGetLocales(int fd);
-
- private static native int nativeGetVersion(int fd);
-}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index ae6a2fd..23d1237 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -25,6 +25,13 @@
* Cookies are manipulated according to RFC2109.
*/
public abstract class CookieManager {
+ /**
+ * @deprecated This class should not be constructed by applications, use {@link #getInstance}
+ * instead to fetch the singleton instance.
+ */
+ // TODO(ntfschr): mark this as @SystemApi after a year.
+ @Deprecated
+ public CookieManager() {}
@Override
protected Object clone() throws CloneNotSupportedException {
diff --git a/core/java/android/webkit/RenderProcessGoneDetail.java b/core/java/android/webkit/RenderProcessGoneDetail.java
index 0843e26..9beeaa5 100644
--- a/core/java/android/webkit/RenderProcessGoneDetail.java
+++ b/core/java/android/webkit/RenderProcessGoneDetail.java
@@ -22,6 +22,13 @@
**/
public abstract class RenderProcessGoneDetail {
/**
+ * @deprecated This class should not be constructed by applications.
+ */
+ // TODO(ntfschr): mark this as @SystemApi after a year.
+ @Deprecated
+ public RenderProcessGoneDetail() {}
+
+ /**
* Indicates whether the render process was observed to crash, or whether
* it was killed by the system.
*
diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java
index 7839a00..ca33a0c 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -27,6 +27,12 @@
* {@link android.webkit.WebView#getSafeBrowsingPrivacyPolicyUrl()}.
*/
public abstract class SafeBrowsingResponse {
+ /**
+ * @deprecated This class should not be constructed by applications.
+ */
+ // TODO(ntfschr): mark this as @SystemApi after a year.
+ @Deprecated
+ public SafeBrowsingResponse() {}
/**
* Display the default interstitial.
diff --git a/core/java/android/webkit/ServiceWorkerController.java b/core/java/android/webkit/ServiceWorkerController.java
index 3517c74..d7e0715 100644
--- a/core/java/android/webkit/ServiceWorkerController.java
+++ b/core/java/android/webkit/ServiceWorkerController.java
@@ -38,6 +38,14 @@
public abstract class ServiceWorkerController {
/**
+ * @deprecated This class should not be constructed by applications, use {@link #getInstance()}
+ * instead to fetch the singleton instance.
+ */
+ // TODO(ntfschr): mark this as @SystemApi after a year.
+ @Deprecated
+ public ServiceWorkerController() {}
+
+ /**
* Returns the default ServiceWorkerController instance. At present there is
* only one ServiceWorkerController instance for all WebView instances,
* however this restriction may be relaxed in the future.
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index 05c0304..9908182 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -36,13 +36,20 @@
* <pre class="prettyprint">
* TracingController tracingController = TracingController.getInstance();
* tracingController.start(new TracingConfig.Builder()
- * .addCategories(CATEGORIES_WEB_DEVELOPER).build());
+ * .addCategories(TracingConfig.CATEGORIES_WEB_DEVELOPER).build());
* ...
* tracingController.stop(new FileOutputStream("trace.json"),
* Executors.newSingleThreadExecutor());
* </pre></p>
*/
public abstract class TracingController {
+ /**
+ * @deprecated This class should not be constructed by applications, use {@link #getInstance}
+ * instead to fetch the singleton instance.
+ */
+ // TODO(ntfschr): mark this as @SystemApi after a year.
+ @Deprecated
+ public TracingController() {}
/**
* Returns the default TracingController instance. At present there is
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index f6166c5..f346c60 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -31,6 +31,14 @@
*/
public abstract class WebViewDatabase {
/**
+ * @deprecated This class should not be constructed by applications, use {@link
+ * #getInstance(Context)} instead to fetch the singleton instance.
+ */
+ // TODO(ntfschr): mark this as @SystemApi after a year.
+ @Deprecated
+ public WebViewDatabase() {}
+
+ /**
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
protected static final String LOGTAG = "webviewdatabase";
diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java
index c503ef2..8ebf303 100644
--- a/core/java/android/widget/DigitalClock.java
+++ b/core/java/android/widget/DigitalClock.java
@@ -27,7 +27,7 @@
import java.util.Calendar;
/**
- * Like AnalogClock, but digital. Shows seconds.
+ * Like AnalogClock, but digital.
*
* @deprecated It is recommended you use {@link TextClock} instead.
*/
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 8dd30f6..9d74c98 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -388,7 +388,8 @@
com.android.internal.R.bool.config_enableHapticTextHandle);
if (FLAG_USE_MAGNIFIER) {
- mMagnifierAnimator = new MagnifierMotionAnimator(new Magnifier(mTextView));
+ final Magnifier magnifier = new Magnifier.Builder(mTextView).build();
+ mMagnifierAnimator = new MagnifierMotionAnimator(magnifier);
}
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0fef9a5..12cc54d 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1338,7 +1338,9 @@
return;
}
if (matrix == null) {
- mDrawable.setBounds(0, 0, getWidth(), getHeight());
+ final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+ final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+ mDrawable.setBounds(0, 0, vwidth, vheight);
} else {
mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
if (mDrawMatrix == null) {
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 8e2786d..16ddd0f 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -119,8 +119,9 @@
*
* @param view the view for which this magnifier is attached
*
- * @see Builder
+ * @deprecated Please use {@link Builder} instead
*/
+ @Deprecated
public Magnifier(@NonNull View view) {
this(new Builder(view));
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bbfac44..f95b3ce 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -321,6 +321,12 @@
* @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
* @attr ref android.R.styleable#TextView_autoSizeStepGranularity
* @attr ref android.R.styleable#TextView_autoSizePresetSizes
+ * @attr ref android.R.styleable#TextView_textCursorDrawable
+ * @attr ref android.R.styleable#TextView_textSelectHandle
+ * @attr ref android.R.styleable#TextView_textSelectHandleLeft
+ * @attr ref android.R.styleable#TextView_textSelectHandleRight
+ * @attr ref android.R.styleable#TextView_allowUndo
+ * @attr ref android.R.styleable#TextView_enabled
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 9171959..0f8295a 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -16,8 +16,7 @@
package com.android.internal.app;
-import com.android.internal.R;
-
+import android.annotation.NonNull;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +31,9 @@
import android.provider.Settings;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Set;
+
/**
* Utility method for dealing with the assistant aspects of
* {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
@@ -62,6 +64,30 @@
return false;
}
+ /**
+ * Checks the availability of a set of voice actions for the current active voice service.
+ *
+ * @param voiceActions A set of supported voice actions to be checked.
+ * @param callback The callback which will deliver a set of supported voice actions. If
+ * no voice actions are supported for the given voice action set, then null
+ * or empty set is provided.
+ */
+ public void getActiveServiceSupportedActions(@NonNull Set<String> voiceActions,
+ @NonNull IVoiceActionCheckCallback callback) {
+ try {
+ if (mVoiceInteractionManagerService != null) {
+ mVoiceInteractionManagerService
+ .getActiveServiceSupportedActions(new ArrayList<>(voiceActions), callback);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call activeServiceSupportedActions", e);
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+
public void launchVoiceAssistFromKeyguard() {
try {
if (mVoiceInteractionManagerService != null) {
@@ -157,7 +183,7 @@
return getActiveServiceComponentName();
}
final SearchManager searchManager =
- (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
if (searchManager == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
copy to core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
index c50d6d6..66ba93d 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
+++ b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -11,15 +11,11 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.stackdivider.events;
+package com.android.internal.app;
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Sent when the divider isn't draging anymore.
- */
-public class StoppedDragingEvent extends EventBus.Event {
+oneway interface IVoiceActionCheckCallback {
+ void onComplete(in List<String> voiceActions);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ff75a8b..5088cca 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.os.Bundle;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -143,4 +144,11 @@
* Register a voice interaction listener.
*/
void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
+
+ /**
+ * Checks the availability of a set of voice actions for the current active voice service.
+ * Returns all supported voice actions.
+ */
+ void getActiveServiceSupportedActions(in List<String> voiceActions,
+ in IVoiceActionCheckCallback callback);
}
diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
index 44867c7..0c203ab 100644
--- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
@@ -24,4 +24,14 @@
byte[] getCurrentStats(out List<ParcelFileDescriptor> historic);
ParcelFileDescriptor getStatsOverTime(long minTime);
int getCurrentMemoryState();
+
+ /**
+ * Get stats committed after highWaterMarkMs
+ * @param highWaterMarkMs Report stats committed after this time.
+ * @param section Integer mask to indicate which sections to include in the stats.
+ * @param doAggregate Whether to aggregate the stats or keep them separated.
+ * @param List of Files of individual commits in protobuf binary or one that is merged from them.
+ */
+ long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate,
+ out List<ParcelFileDescriptor> committedStats);
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index f6e99ba..e7ac566 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -158,6 +158,25 @@
STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
};
+ // Should report process stats.
+ public static final int REPORT_PROC_STATS = 0x01;
+ // Should report package process stats.
+ public static final int REPORT_PKG_PROC_STATS = 0x02;
+ // Should report package service stats.
+ public static final int REPORT_PKG_SVC_STATS = 0x04;
+ // Should report package association stats.
+ public static final int REPORT_PKG_ASC_STATS = 0x08;
+ // Should report package stats.
+ public static final int REPORT_PKG_STATS = 0x0E;
+ // Should report all stats.
+ public static final int REPORT_ALL = 0x0F;
+
+ public static final int[] OPTIONS =
+ {REPORT_PROC_STATS, REPORT_PKG_PROC_STATS, REPORT_PKG_SVC_STATS, REPORT_PKG_ASC_STATS,
+ REPORT_PKG_STATS, REPORT_ALL};
+ public static final String[] OPTIONS_STR =
+ {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
+
// Current version of the parcel format.
private static final int PARCEL_VERSION = 34;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
@@ -1412,7 +1431,7 @@
}
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
- boolean dumpDetails, boolean dumpAll, boolean activeOnly) {
+ boolean dumpDetails, boolean dumpAll, boolean activeOnly, int section) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
boolean sepNeeded = false;
@@ -1421,176 +1440,205 @@
mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
sepNeeded = true;
}
- ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap();
boolean printedHeader = false;
- for (int ip=0; ip<pkgMap.size(); ip++) {
- final String pkgName = pkgMap.keyAt(ip);
- final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
- for (int iv=0; iv<vpkgs.size(); iv++) {
- final long vers = vpkgs.keyAt(iv);
- final PackageState pkgState = vpkgs.valueAt(iv);
- final int NPROCS = pkgState.mProcesses.size();
- final int NSRVS = pkgState.mServices.size();
- final int NASCS = pkgState.mAssociations.size();
- final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
- if (!pkgMatch) {
- boolean procMatch = false;
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (reqPackage.equals(proc.getName())) {
- procMatch = true;
- break;
+ if ((section & REPORT_PKG_STATS) != 0) {
+ ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+ mPackages.getMap();
+ for (int ip = 0; ip < pkgMap.size(); ip++) {
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv = 0; iv < vpkgs.size(); iv++) {
+ final long vers = vpkgs.keyAt(iv);
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ final int NASCS = pkgState.mAssociations.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ if (!pkgMatch) {
+ boolean procMatch = false;
+ for (int iproc = 0; iproc < NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (reqPackage.equals(proc.getName())) {
+ procMatch = true;
+ break;
+ }
}
- }
- if (!procMatch) {
- continue;
- }
- }
- if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
- if (!printedHeader) {
- if (sepNeeded) pw.println();
- pw.println("Per-Package Stats:");
- printedHeader = true;
- sepNeeded = true;
- }
- pw.print(" * "); pw.print(pkgName); pw.print(" / ");
- UserHandle.formatUid(pw, uid); pw.print(" / v");
- pw.print(vers); pw.println(":");
- }
- if (!dumpSummary || dumpAll) {
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+ if (!procMatch) {
continue;
}
- if (activeOnly && !proc.isInUse()) {
- pw.print(" (Not active: ");
- pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
- continue;
+ }
+ if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
+ if (!printedHeader) {
+ if (sepNeeded) pw.println();
+ pw.println("Per-Package Stats:");
+ printedHeader = true;
+ sepNeeded = true;
}
- pw.print(" Process ");
- pw.print(pkgState.mProcesses.keyAt(iproc));
- if (proc.getCommonProcess().isMultiPackage()) {
- pw.print(" (multi, ");
- } else {
- pw.print(" (unique, ");
- }
- pw.print(proc.getDurationsBucketCount());
- pw.print(" entries)");
+ pw.print(" * ");
+ pw.print(pkgName);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(" / v");
+ pw.print(vers);
pw.println(":");
- proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- proc.dumpInternalLocked(pw, " ", dumpAll);
}
- } else {
- ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.getName())) {
- continue;
+ if ((section & REPORT_PKG_PROC_STATS) != 0) {
+ if (!dumpSummary || dumpAll) {
+ for (int iproc = 0; iproc < NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ pw.print(" (Not active: ");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.println(")");
+ continue;
+ }
+ pw.print(" Process ");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ if (proc.getCommonProcess().isMultiPackage()) {
+ pw.print(" (multi, ");
+ } else {
+ pw.print(" (unique, ");
+ }
+ pw.print(proc.getDurationsBucketCount());
+ pw.print(" entries)");
+ pw.println(":");
+ proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ proc.dumpInternalLocked(pw, " ", dumpAll);
+ }
+ } else {
+ ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
+ for (int iproc = 0; iproc < NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ continue;
+ }
+ procs.add(proc);
+ }
+ DumpUtils.dumpProcessSummaryLocked(pw, " ", "Prc ", procs,
+ ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
+ now, totalTime);
}
- if (activeOnly && !proc.isInUse()) {
- continue;
+ }
+ if ((section & REPORT_PKG_SVC_STATS) != 0) {
+ for (int isvc = 0; isvc < NSRVS; isvc++) {
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
+ continue;
+ }
+ if (activeOnly && !svc.isInUse()) {
+ pw.print(" (Not active service: ");
+ pw.print(pkgState.mServices.keyAt(isvc));
+ pw.println(")");
+ continue;
+ }
+ if (dumpAll) {
+ pw.print(" Service ");
+ } else {
+ pw.print(" * Svc ");
+ }
+ pw.print(pkgState.mServices.keyAt(isvc));
+ pw.println(":");
+ pw.print(" Process: ");
+ pw.println(svc.getProcessName());
+ svc.dumpStats(pw, " ", " ", " ",
+ now, totalTime, dumpSummary, dumpAll);
}
- procs.add(proc);
}
- DumpUtils.dumpProcessSummaryLocked(pw, " ", "Prc ", procs,
- ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
- now, totalTime);
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- ServiceState svc = pkgState.mServices.valueAt(isvc);
- if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
- continue;
+ if ((section & REPORT_PKG_ASC_STATS) != 0) {
+ for (int iasc = 0; iasc < NASCS; iasc++) {
+ AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+ if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
+ continue;
+ }
+ if (activeOnly && !asc.isInUse()) {
+ pw.print(" (Not active association: ");
+ pw.print(pkgState.mAssociations.keyAt(iasc));
+ pw.println(")");
+ continue;
+ }
+ if (dumpAll) {
+ pw.print(" Association ");
+ } else {
+ pw.print(" * Asc ");
+ }
+ pw.print(pkgState.mAssociations.keyAt(iasc));
+ pw.println(":");
+ pw.print(" Process: ");
+ pw.println(asc.getProcessName());
+ asc.dumpStats(pw, " ", " ", " ",
+ now, totalTime, dumpDetails, dumpAll);
+ }
}
- if (activeOnly && !svc.isInUse()) {
- pw.print(" (Not active service: ");
- pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
- continue;
- }
- if (dumpAll) {
- pw.print(" Service ");
- } else {
- pw.print(" * Svc ");
- }
- pw.print(pkgState.mServices.keyAt(isvc));
- pw.println(":");
- pw.print(" Process: "); pw.println(svc.getProcessName());
- svc.dumpStats(pw, " ", " ", " ",
- now, totalTime, dumpSummary, dumpAll);
- }
- for (int iasc=0; iasc<NASCS; iasc++) {
- AssociationState asc = pkgState.mAssociations.valueAt(iasc);
- if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
- continue;
- }
- if (activeOnly && !asc.isInUse()) {
- pw.print(" (Not active association: ");
- pw.print(pkgState.mAssociations.keyAt(iasc)); pw.println(")");
- continue;
- }
- if (dumpAll) {
- pw.print(" Association ");
- } else {
- pw.print(" * Asc ");
- }
- pw.print(pkgState.mAssociations.keyAt(iasc));
- pw.println(":");
- pw.print(" Process: "); pw.println(asc.getProcessName());
- asc.dumpStats(pw, " ", " ", " ",
- now, totalTime, dumpDetails, dumpAll);
}
}
}
}
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- printedHeader = false;
- int numShownProcs = 0, numTotalProcs = 0;
- for (int ip=0; ip<procMap.size(); ip++) {
- String procName = procMap.keyAt(ip);
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- numTotalProcs++;
- final ProcessState proc = uids.valueAt(iu);
- if (!proc.hasAnyData()) {
- continue;
+ if ((section & REPORT_PROC_STATS) != 0) {
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ printedHeader = false;
+ int numShownProcs = 0, numTotalProcs = 0;
+ for (int ip = 0; ip < procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ numTotalProcs++;
+ final ProcessState proc = uids.valueAt(iu);
+ if (!proc.hasAnyData()) {
+ continue;
+ }
+ if (!proc.isMultiPackage()) {
+ continue;
+ }
+ if (reqPackage != null && !reqPackage.equals(procName)
+ && !reqPackage.equals(proc.getPackage())) {
+ continue;
+ }
+ numShownProcs++;
+ if (sepNeeded) {
+ pw.println();
+ }
+ sepNeeded = true;
+ if (!printedHeader) {
+ pw.println("Multi-Package Common Processes:");
+ printedHeader = true;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ pw.print(" (Not active: ");
+ pw.print(procName);
+ pw.println(")");
+ continue;
+ }
+ pw.print(" * ");
+ pw.print(procName);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(" (");
+ pw.print(proc.getDurationsBucketCount());
+ pw.print(" entries)");
+ pw.println(":");
+ proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now);
+ proc.dumpInternalLocked(pw, " ", dumpAll);
}
- if (!proc.isMultiPackage()) {
- continue;
- }
- if (reqPackage != null && !reqPackage.equals(procName)
- && !reqPackage.equals(proc.getPackage())) {
- continue;
- }
- numShownProcs++;
- if (sepNeeded) {
- pw.println();
- }
- sepNeeded = true;
- if (!printedHeader) {
- pw.println("Multi-Package Common Processes:");
- printedHeader = true;
- }
- if (activeOnly && !proc.isInUse()) {
- pw.print(" (Not active: "); pw.print(procName); pw.println(")");
- continue;
- }
- pw.print(" * "); pw.print(procName); pw.print(" / ");
- UserHandle.formatUid(pw, uid);
- pw.print(" ("); pw.print(proc.getDurationsBucketCount());
- pw.print(" entries)"); pw.println(":");
- proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now);
- proc.dumpInternalLocked(pw, " ", dumpAll);
}
+ pw.print(" Total procs: "); pw.print(numShownProcs);
+ pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
}
if (dumpAll) {
@@ -1598,8 +1646,7 @@
pw.println();
}
sepNeeded = true;
- pw.print(" Total procs: "); pw.print(numShownProcs);
- pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+
if (mTrackingAssociations.size() > 0) {
pw.println();
pw.println("Tracking associations:");
@@ -1866,7 +1913,10 @@
return outProcs;
}
- public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
+ /**
+ * Prints checkin style stats dump.
+ */
+ public void dumpCheckinLocked(PrintWriter pw, String reqPackage, int section) {
final long now = SystemClock.uptimeMillis();
final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
mPackages.getMap();
@@ -1895,50 +1945,61 @@
}
pw.println();
pw.print("config,"); pw.println(mRuntime);
- for (int ip=0; ip<pkgMap.size(); ip++) {
- final String pkgName = pkgMap.keyAt(ip);
- if (reqPackage != null && !reqPackage.equals(pkgName)) {
- continue;
- }
- final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
- for (int iv=0; iv<vpkgs.size(); iv++) {
- final long vers = vpkgs.keyAt(iv);
- final PackageState pkgState = vpkgs.valueAt(iv);
- final int NPROCS = pkgState.mProcesses.size();
- final int NSRVS = pkgState.mServices.size();
- final int NASCS = pkgState.mAssociations.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
- pkgState.mProcesses.keyAt(iproc), now);
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- final String serviceName = DumpUtils.collapseString(pkgName,
- pkgState.mServices.keyAt(isvc));
- final ServiceState svc = pkgState.mServices.valueAt(isvc);
- svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
- }
- for (int iasc=0; iasc<NASCS; iasc++) {
- final String associationName = DumpUtils.collapseString(pkgName,
- pkgState.mAssociations.keyAt(iasc));
- final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
- asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now);
+
+ if ((section & REPORT_PKG_STATS) != 0) {
+ for (int ip = 0; ip < pkgMap.size(); ip++) {
+ final String pkgName = pkgMap.keyAt(ip);
+ if (reqPackage != null && !reqPackage.equals(pkgName)) {
+ continue;
+ }
+ final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv = 0; iv < vpkgs.size(); iv++) {
+ final long vers = vpkgs.keyAt(iv);
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ final int NASCS = pkgState.mAssociations.size();
+ if ((section & REPORT_PKG_PROC_STATS) != 0) {
+ for (int iproc = 0; iproc < NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
+ pkgState.mProcesses.keyAt(iproc), now);
+ }
+ }
+ if ((section & REPORT_PKG_SVC_STATS) != 0) {
+ for (int isvc = 0; isvc < NSRVS; isvc++) {
+ final String serviceName = DumpUtils.collapseString(pkgName,
+ pkgState.mServices.keyAt(isvc));
+ final ServiceState svc = pkgState.mServices.valueAt(isvc);
+ svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
+ }
+ }
+ if ((section & REPORT_PKG_ASC_STATS) != 0) {
+ for (int iasc = 0; iasc < NASCS; iasc++) {
+ final String associationName = DumpUtils.collapseString(pkgName,
+ pkgState.mAssociations.keyAt(iasc));
+ final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+ asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now);
+ }
+ }
}
}
}
}
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- for (int ip=0; ip<procMap.size(); ip++) {
- String procName = procMap.keyAt(ip);
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final ProcessState procState = uids.valueAt(iu);
- procState.dumpProcCheckin(pw, procName, uid, now);
+ if ((section & REPORT_PROC_STATS) != 0) {
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip = 0; ip < procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ procState.dumpProcCheckin(pw, procName, uid, now);
+ }
}
}
pw.print("total");
@@ -2013,9 +2074,10 @@
}
}
- public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
- final long token = proto.start(fieldId);
-
+ /**
+ * Writes to ProtoOutputStream.
+ */
+ public void writeToProto(ProtoOutputStream proto, long now, int section) {
proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
@@ -2024,15 +2086,15 @@
proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
boolean partial = true;
- if ((mFlags&FLAG_SHUTDOWN) != 0) {
+ if ((mFlags & FLAG_SHUTDOWN) != 0) {
proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
partial = false;
}
- if ((mFlags&FLAG_SYSPROPS) != 0) {
+ if ((mFlags & FLAG_SYSPROPS) != 0) {
proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
partial = false;
}
- if ((mFlags&FLAG_COMPLETE) != 0) {
+ if ((mFlags & FLAG_COMPLETE) != 0) {
proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
partial = false;
}
@@ -2041,31 +2103,34 @@
}
final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- for (int ip=0; ip<procMap.size(); ip++) {
- final String procName = procMap.keyAt(ip);
- final SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final ProcessState procState = uids.valueAt(iu);
- procState.writeToProto(proto, ProcessStatsSectionProto.PROCESS_STATS, procName,
- uid, now);
- }
- }
-
- final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
- mPackages.getMap();
- for (int ip = 0; ip < pkgMap.size(); ip++) {
- final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu = 0; iu < uids.size(); iu++) {
- final LongSparseArray<PackageState> vers = uids.valueAt(iu);
- for (int iv = 0; iv < vers.size(); iv++) {
- final PackageState pkgState = vers.valueAt(iv);
- pkgState.writeToProto(proto, ProcessStatsSectionProto.PACKAGE_STATS, now);
+ if ((section & REPORT_PROC_STATS) != 0) {
+ for (int ip = 0; ip < procMap.size(); ip++) {
+ final String procName = procMap.keyAt(ip);
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ procState.writeToProto(proto, ProcessStatsSectionProto.PROCESS_STATS, procName,
+ uid, now);
}
}
}
- proto.end(token);
+ if ((section & REPORT_PKG_STATS) != 0) {
+ final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+ mPackages.getMap();
+ for (int ip = 0; ip < pkgMap.size(); ip++) {
+ final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ final LongSparseArray<PackageState> vers = uids.valueAt(iu);
+ for (int iv = 0; iv < vers.size(); iv++) {
+ final PackageState pkgState = vers.valueAt(iv);
+ pkgState.writeToProto(proto, ProcessStatsSectionProto.PACKAGE_STATS, now,
+ section);
+ }
+ }
+ }
+ }
}
final public static class ProcessStateHolder {
@@ -2110,30 +2175,39 @@
return as;
}
- public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
+ /**
+ * Writes the containing stats into proto, with options to choose smaller sections.
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId, long now, int section) {
final long token = proto.start(fieldId);
proto.write(ProcessStatsPackageProto.PACKAGE, mPackageName);
proto.write(ProcessStatsPackageProto.UID, mUid);
proto.write(ProcessStatsPackageProto.VERSION, mVersionCode);
- for (int ip = 0; ip < mProcesses.size(); ip++) {
- final String procName = mProcesses.keyAt(ip);
- final ProcessState procState = mProcesses.valueAt(ip);
- procState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, procName,
- mUid, now);
+ if ((section & ProcessStats.REPORT_PKG_PROC_STATS) != 0) {
+ for (int ip = 0; ip < mProcesses.size(); ip++) {
+ final String procName = mProcesses.keyAt(ip);
+ final ProcessState procState = mProcesses.valueAt(ip);
+ procState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, procName,
+ mUid, now);
+ }
}
- for (int is = 0; is < mServices.size(); is++) {
- final ServiceState serviceState = mServices.valueAt(is);
- serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS,
- now);
+ if ((section & ProcessStats.REPORT_PKG_SVC_STATS) != 0) {
+ for (int is = 0; is < mServices.size(); is++) {
+ final ServiceState serviceState = mServices.valueAt(is);
+ serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS,
+ now);
+ }
}
- for (int ia=0; ia<mAssociations.size(); ia++) {
- final AssociationState ascState = mAssociations.valueAt(ia);
- ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS,
- now);
+ if ((section & ProcessStats.REPORT_PKG_ASC_STATS) != 0) {
+ for (int ia = 0; ia < mAssociations.size(); ia++) {
+ final AssociationState ascState = mAssociations.valueAt(ia);
+ ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS,
+ now);
+ }
}
proto.end(token);
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index cb282b6..0080ace 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -66,13 +66,13 @@
return !TextUtils.isEmpty(doubleTapSensorType());
}
- public boolean reachGestureEnabled(int user) {
- return boolSettingDefaultOn(Settings.Secure.DOZE_REACH_GESTURE, user)
- && reachGestureAvailable();
+ public boolean wakeLockScreenGestureEnabled(int user) {
+ return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
+ && wakeLockScreenGestureAvailable();
}
- public boolean reachGestureAvailable() {
- return !TextUtils.isEmpty(reachSensorType());
+ public boolean wakeLockScreenGestureAvailable() {
+ return !TextUtils.isEmpty(wakeLockScreenSensorType());
}
public boolean wakeScreenGestureEnabled(int user) {
@@ -92,8 +92,8 @@
return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
}
- public String reachSensorType() {
- return mContext.getResources().getString(R.string.config_dozeReachSensorType);
+ public String wakeLockScreenSensorType() {
+ return mContext.getResources().getString(R.string.config_dozeWakeLockScreenSensorType);
}
public String wakeScreenSensorType() {
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index c32894d..2671781 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -28,7 +28,6 @@
interface IInputMethodPrivilegedOperations {
void setImeWindowStatus(int vis, int backDisposition);
void reportStartInput(in IBinder startInputToken);
- void clearLastInputMethodWindowForTransition();
IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName);
void reportFullscreenMode(boolean fullscreen);
void setInputMethod(String id);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index f0e8dc5..cdb986a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -135,22 +135,6 @@
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#clearLastInputMethodWindowForTransition()}.
- */
- @AnyThread
- public void clearLastInputMethodWindowForTransition() {
- final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.clearLastInputMethodWindowForTransition();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}.
*
* @param contentUri Content URI to which a temporary read permission should be granted
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 31f13c3..33b9ff7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -337,6 +337,9 @@
private final PlatformIdleStateCallback mPlatformIdleStateCallback;
+ /**
+ * This handler is running on {@link BackgroundThread}.
+ */
final class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper, null, true);
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 0650d0af..e4724ff 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -17,6 +17,7 @@
package com.android.internal.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -36,7 +37,7 @@
* @hide Only for use within the system server.
*/
public class LooperStats implements Looper.Observer {
- private static final int TOKEN_POOL_SIZE = 50;
+ private static final int SESSION_POOL_SIZE = 50;
@GuardedBy("mLock")
private final SparseArray<Entry> mEntries = new SparseArray<>(512);
@@ -78,17 +79,19 @@
}
DispatchSession session = (DispatchSession) token;
- Entry entry = getOrCreateEntry(msg);
- synchronized (entry) {
- entry.messageCount++;
- if (session != DispatchSession.NOT_SAMPLED) {
- entry.recordedMessageCount++;
- long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
- long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
- entry.totalLatencyMicro += latency;
- entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
- entry.cpuUsageMicro += cpuUsage;
- entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+ Entry entry = findEntry(msg, /* allowCreateNew= */session != DispatchSession.NOT_SAMPLED);
+ if (entry != null) {
+ synchronized (entry) {
+ entry.messageCount++;
+ if (session != DispatchSession.NOT_SAMPLED) {
+ entry.recordedMessageCount++;
+ long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
+ long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
+ entry.totalLatencyMicro += latency;
+ entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
+ entry.cpuUsageMicro += cpuUsage;
+ entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+ }
}
}
@@ -102,7 +105,7 @@
}
DispatchSession session = (DispatchSession) token;
- Entry entry = getOrCreateEntry(msg);
+ Entry entry = findEntry(msg, /* allowCreateNew= */true);
synchronized (entry) {
entry.exceptionCount++;
}
@@ -159,24 +162,28 @@
mSamplingInterval = samplingInterval;
}
- @NonNull
- private Entry getOrCreateEntry(Message msg) {
+ @Nullable
+ private Entry findEntry(Message msg, boolean allowCreateNew) {
final boolean isInteractive = mDeviceState.isScreenInteractive();
final int id = Entry.idFor(msg, isInteractive);
Entry entry;
synchronized (mLock) {
entry = mEntries.get(id);
if (entry == null) {
- if (mEntries.size() >= mEntriesSizeCap) {
- // If over the size cap, track totals under a single entry.
+ if (!allowCreateNew) {
+ return null;
+ } else if (mEntries.size() >= mEntriesSizeCap) {
+ // If over the size cap track totals under OVERFLOW entry.
return mOverflowEntry;
+ } else {
+ entry = new Entry(msg, isInteractive);
+ mEntries.put(id, entry);
}
- entry = new Entry(msg, isInteractive);
- mEntries.put(id, entry);
}
}
- if (entry.handler.getClass() != msg.getTarget().getClass()
+ if (entry.workSourceUid != msg.workSourceUid
+ || entry.handler.getClass() != msg.getTarget().getClass()
|| entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread()
|| entry.isInteractive != isInteractive) {
// If a hash collision happened, track totals under a single entry.
@@ -186,7 +193,7 @@
}
private void recycleSession(DispatchSession session) {
- if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) {
+ if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < SESSION_POOL_SIZE) {
mSessionPool.add(session);
}
}
@@ -210,6 +217,7 @@
}
private static class Entry {
+ public final int workSourceUid;
public final Handler handler;
public final String messageName;
public final boolean isInteractive;
@@ -222,12 +230,14 @@
public long maxCpuUsageMicro;
Entry(Message msg, boolean isInteractive) {
+ this.workSourceUid = msg.workSourceUid;
this.handler = msg.getTarget();
this.messageName = handler.getMessageName(msg);
this.isInteractive = isInteractive;
}
Entry(String specialEntryName) {
+ this.workSourceUid = Message.UID_NONE;
this.messageName = specialEntryName;
this.handler = null;
this.isInteractive = false;
@@ -245,6 +255,7 @@
static int idFor(Message msg, boolean isInteractive) {
int result = 7;
+ result = 31 * result + msg.workSourceUid;
result = 31 * result + msg.getTarget().getLooper().getThread().hashCode();
result = 31 * result + msg.getTarget().getClass().hashCode();
result = 31 * result + (isInteractive ? 1231 : 1237);
@@ -258,6 +269,7 @@
/** Aggregated data of Looper message dispatching in the in the current process. */
public static class ExportedEntry {
+ public final int workSourceUid;
public final String handlerClassName;
public final String threadName;
public final String messageName;
@@ -271,6 +283,7 @@
public final long maxCpuUsageMicros;
ExportedEntry(Entry entry) {
+ this.workSourceUid = entry.workSourceUid;
if (entry.handler != null) {
this.handlerClassName = entry.handler.getClass().getName();
this.threadName = entry.handler.getLooper().getThread().getName();
diff --git a/core/java/com/android/internal/os/StoragedUidIoStatsReader.java b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
new file mode 100644
index 0000000..9b03469
--- /dev/null
+++ b/core/java/com/android/internal/os/StoragedUidIoStatsReader.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.internal.os;
+
+import android.os.StrictMode;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+
+/**
+ * Reads /proc/uid_io/stats which has the line format:
+ *
+ * uid: foreground_read_chars foreground_write_chars foreground_read_bytes foreground_write_bytes
+ * background_read_chars background_write_chars background_read_bytes background_write_bytes
+ * foreground_fsync background_fsync
+ *
+ * This provides the number of bytes/chars read/written in foreground/background for each uid.
+ * The file contains a monotonically increasing count of bytes/chars for a single boot.
+ */
+public class StoragedUidIoStatsReader {
+
+ private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName();
+ private static String sUidIoFile = "/proc/uid_io/stats";
+
+ public StoragedUidIoStatsReader() {
+ }
+
+ @VisibleForTesting
+ public StoragedUidIoStatsReader(String file) {
+ sUidIoFile = file;
+ }
+
+ /**
+ * Notifies when new data is available.
+ */
+ public interface Callback {
+
+ /**
+ * Provides data to the client.
+ *
+ * Note: Bytes are I/O events from a storage device. Chars are data requested by syscalls,
+ * and can be satisfied by caching.
+ */
+ void onUidStorageStats(int uid, long fgCharsRead, long fgCharsWrite, long fgBytesRead,
+ long fgBytesWrite, long bgCharsRead, long bgCharsWrite, long bgBytesRead,
+ long bgBytesWrite, long fgFsync, long bgFsync);
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with raw absolute value of I/O stats
+ * for each UID.
+ *
+ * @param callback The callback to invoke for each line of the proc file.
+ */
+ public void readAbsolute(Callback callback) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ File file = new File(sUidIoFile);
+ try (BufferedReader reader = Files.newBufferedReader(file.toPath())) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] fields = TextUtils.split(line, " ");
+ if (fields.length != 11) {
+ Slog.e(TAG, "Malformed entry in " + sUidIoFile + ": " + line);
+ continue;
+ }
+ try {
+ final String uidStr = fields[0];
+ final int uid = Integer.parseInt(fields[0], 10);
+ final long fgCharsRead = Long.parseLong(fields[1], 10);
+ final long fgCharsWrite = Long.parseLong(fields[2], 10);
+ final long fgBytesRead = Long.parseLong(fields[3], 10);
+ final long fgBytesWrite = Long.parseLong(fields[4], 10);
+ final long bgCharsRead = Long.parseLong(fields[5], 10);
+ final long bgCharsWrite = Long.parseLong(fields[6], 10);
+ final long bgBytesRead = Long.parseLong(fields[7], 10);
+ final long bgBytesWrite = Long.parseLong(fields[8], 10);
+ final long fgFsync = Long.parseLong(fields[9], 10);
+ final long bgFsync = Long.parseLong(fields[10], 10);
+ callback.onUidStorageStats(uid, fgCharsRead, fgCharsWrite, fgBytesRead,
+ fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
+ fgFsync, bgFsync);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse entry in " + sUidIoFile + ": " + e.getMessage());
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage());
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 927322e..98b7b5d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -133,15 +133,16 @@
* if this is the parent, or -1 on error.
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName) {
+ int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName, String[] packagesForUid, String[] visibleVolIds) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
+ packagesForUid, visibleVolIds);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -154,9 +155,9 @@
}
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName);
+ int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName, String[] packagesForUid, String[] visibleVolIds);
/**
* Called to do any initialization before starting an application.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 06c41d8..4a94ec4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -241,7 +241,8 @@
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
- parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName);
+ parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName,
+ parsedArgs.packagesForUid, parsedArgs.visibleVolIds);
try {
if (pid == 0) {
@@ -432,6 +433,12 @@
/** from --package-name */
String packageName;
+ /** from --packages-for-uid */
+ String[] packagesForUid;
+
+ /** from --visible-vols */
+ String[] visibleVolIds;
+
/**
* Any args after and including the first non-option arg
* (or after a '--')
@@ -687,6 +694,10 @@
throw new IllegalArgumentException("Duplicate arg specified");
}
packageName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--packages-for-uid=")) {
+ packagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
+ } else if (arg.startsWith("--visible-vols=")) {
+ visibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
} else {
break;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c4214cf..9b8f120 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -141,7 +141,8 @@
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, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index e48e733..90f2002 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -91,7 +91,8 @@
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, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 1038199..a403c06 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -523,8 +523,9 @@
}
public static int resolveAmbientColor(Context context, int notificationColor) {
- final int resolvedColor = resolveColor(context, notificationColor,
- true /* defaultBackgroundIsDark */);
+ final int resolvedColor = notificationColor == Notification.COLOR_DEFAULT
+ ? context.getColor(com.android.internal.R.color.notification_default_color_dark)
+ : notificationColor;
int color = resolvedColor;
color = ContrastColorUtil.ensureTextContrastOnBlack(color);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 543f4a5..5f1243f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -31,6 +31,8 @@
* applications.
*/
interface IInputMethodManager {
+ void addClient(in IInputMethodClient client, in IInputContext inputContext);
+
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
List<InputMethodInfo> getVrInputMethodList();
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 7548c22..101fd41 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -139,8 +139,7 @@
* The client should try to restart input when its {@link android.view.Window} is focused
* again.</p>
*
- * @see com.android.server.wm.WindowManagerInternal#inputMethodClientHasFocus(
- * IInputMethodClient)
+ * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
*/
int ERROR_NOT_IME_TARGET_WINDOW = 11;
/**
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 0a25271..897f6fa5 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -15,7 +15,6 @@
#include "SkPM4f.h"
#include "SkPM4fPriv.h"
#include "GraphicsJNI.h"
-#include "SkDither.h"
#include "SkUnPreMultiply.h"
#include "SkStream.h"
@@ -326,33 +325,6 @@
return true;
}
-//////////////////// ToColor procs
-
-static void ToColor_SA8(SkColor dst[], const void* src, int width) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- do {
- uint8_t c = *s++;
- *dst++ = SkColorSetARGB(c, 0, 0, 0);
- } while (--width != 0);
-}
-
-static void ToF16_SA8(void* dst, const void* src, int width) {
- SkASSERT(width > 0);
- uint64_t* d = (uint64_t*)dst;
- const uint8_t* s = (const uint8_t*)src;
-
- for (int i = 0; i < width; i++) {
- uint8_t c = *s++;
- SkPM4f a;
- a.fVec[SkPM4f::R] = 0.0f;
- a.fVec[SkPM4f::G] = 0.0f;
- a.fVec[SkPM4f::B] = 0.0f;
- a.fVec[SkPM4f::A] = c / 255.0f;
- *d++ = a.toF16();
- }
-}
-
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@@ -417,23 +389,12 @@
SkImageInfo dstInfo = srcPM.info().makeColorType(dstCT);
switch (dstCT) {
case kRGB_565_SkColorType:
- // copyTo() has never been strict on alpha type. Here we set the src to opaque to
- // allow the call to readPixels() to succeed and preserve this lenient behavior.
- if (kOpaque_SkAlphaType != srcPM.alphaType()) {
- srcPM = SkPixmap(srcPM.info().makeAlphaType(kOpaque_SkAlphaType), srcPM.addr(),
- srcPM.rowBytes());
- dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
- }
+ dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
break;
case kRGBA_F16_SkColorType:
// The caller does not have an opportunity to pass a dst color space. Assume that
// they want linear sRGB.
dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
-
- if (!srcPM.colorSpace()) {
- // Skia needs a color space to convert to F16. nullptr should be treated as sRGB.
- srcPM.setColorSpace(SkColorSpace::MakeSRGB());
- }
break;
default:
break;
@@ -446,57 +407,11 @@
return false;
}
- // Skia does not support copying from kAlpha8 to types that are not alpha only.
- // We will handle this case here.
- if (kAlpha_8_SkColorType == srcPM.colorType() && kAlpha_8_SkColorType != dstCT) {
- switch (dstCT) {
- case kRGBA_8888_SkColorType:
- case kBGRA_8888_SkColorType: {
- for (int y = 0; y < src.height(); y++) {
- const uint8_t* srcRow = srcPM.addr8(0, y);
- uint32_t* dstRow = dst->getAddr32(0, y);
- ToColor_SA8(dstRow, srcRow, src.width());
- }
- return true;
- }
- case kRGB_565_SkColorType: {
- for (int y = 0; y < src.height(); y++) {
- uint16_t* dstRow = dst->getAddr16(0, y);
- memset(dstRow, 0, sizeof(uint16_t) * src.width());
- }
- return true;
- }
- case kRGBA_F16_SkColorType: {
- for (int y = 0; y < src.height(); y++) {
- const uint8_t* srcRow = srcPM.addr8(0, y);
- void* dstRow = dst->getAddr(0, y);
- ToF16_SA8(dstRow, srcRow, src.width());
- }
- return true;
- }
- default:
- return false;
- }
- }
-
SkPixmap dstPM;
if (!dst->peekPixels(&dstPM)) {
return false;
}
- // Skia needs a color space to convert from F16. nullptr should be treated as sRGB.
- if (kRGBA_F16_SkColorType == srcPM.colorType() && !dstPM.colorSpace()) {
- dstPM.setColorSpace(SkColorSpace::MakeSRGB());
- }
-
- // readPixels does not support color spaces with parametric transfer functions. This
- // works around that restriction when the color spaces are equal.
- if (kRGBA_F16_SkColorType != dstCT && kRGBA_F16_SkColorType != srcPM.colorType() &&
- dstPM.colorSpace() == srcPM.colorSpace()) {
- dstPM.setColorSpace(nullptr);
- srcPM.setColorSpace(nullptr);
- }
-
return srcPM.readPixels(dstPM);
}
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index e2dc52b..5f67d30 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -21,11 +21,11 @@
#include "CreateJavaOutputStreamAdaptor.h"
-#include "SkDocument.h"
+#include "SkPDFDocument.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
-#include "SkStream.h"
#include "SkRect.h"
+#include "SkStream.h"
#include <hwui/Canvas.h>
@@ -88,7 +88,7 @@
}
void write(SkWStream* stream) {
- sk_sp<SkDocument> document = SkDocument::MakePDF(stream);
+ sk_sp<SkDocument> document = SkPDF::MakeDocument(stream);
for (unsigned i = 0; i < mPages.size(); i++) {
PageRecord* page = mPages[i];
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index b163597..a45b493 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -622,13 +622,17 @@
// ---------------------------------------------------------------------------
+// The internal format is no longer the same as pixel format, per Table 2 in
+// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
static int checkInternalFormat(SkColorType colorType, int internalformat,
int type)
{
switch(colorType) {
case kN32_SkColorType:
return (type == GL_UNSIGNED_BYTE &&
- internalformat == GL_RGBA) ? 0 : -1;
+ internalformat == GL_RGBA) ||
+ (type == GL_UNSIGNED_BYTE &&
+ internalformat == GL_SRGB8_ALPHA8) ? 0 : -1;
case kAlpha_8_SkColorType:
return (type == GL_UNSIGNED_BYTE &&
internalformat == GL_ALPHA) ? 0 : -1;
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index eba4c50..343aef2 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -674,7 +674,7 @@
int ret = 0;
ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods));
- ret |= RegisterMethodsOrDie(env, "android/view/RecordingCanvas", gDrawMethods, NELEM(gDrawMethods));
+ ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods));
return ret;
}
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index 6f4a58a..58a2379 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -130,7 +130,7 @@
static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) {
VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- return tree->stagingProperties()->getRootAlpha();
+ return tree->stagingProperties().getRootAlpha();
}
static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr,
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 6df23f7..a1f2377 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -58,6 +58,11 @@
int ret;
int fd;
+ if (name == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index dfa5de6..b70485d 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -28,6 +28,14 @@
android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
}
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) {
+ ScopedUtfChars pathChars(env, path);
+ ScopedUtfChars appNameChars(env, appName);
+ ScopedUtfChars appPrefChars(env, appPref);
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
+ appPrefChars.c_str(), devOptIn);
+}
+
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader(
env, classLoader);
@@ -44,6 +52,7 @@
const JNINativeMethod g_methods[] = {
{ "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+ { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) },
{ "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
{ "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
};
diff --git a/core/jni/android_text_LineBreaker.cpp b/core/jni/android_text_LineBreaker.cpp
index dac108e..5439107 100644
--- a/core/jni/android_text_LineBreaker.cpp
+++ b/core/jni/android_text_LineBreaker.cpp
@@ -41,17 +41,6 @@
namespace android {
-struct JLineBreaksID {
- jfieldID breaks;
- jfieldID widths;
- jfieldID ascents;
- jfieldID descents;
- jfieldID flags;
-};
-
-static jclass gLineBreaks_class;
-static JLineBreaksID gLineBreaks_fieldID;
-
static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
if (javaArray == nullptr) {
return std::vector<float>();
@@ -85,34 +74,7 @@
return reinterpret_cast<jlong>(nFinish);
}
-static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
- jfloatArray recycleWidths, jfloatArray recycleAscents,
- jfloatArray recycleDescents, jintArray recycleFlags,
- jint recycleLength, const minikin::LineBreakResult& result) {
- const size_t nBreaks = result.breakPoints.size();
- if ((size_t)recycleLength < nBreaks) {
- // have to reallocate buffers
- recycleBreaks = env->NewIntArray(nBreaks);
- recycleWidths = env->NewFloatArray(nBreaks);
- recycleAscents = env->NewFloatArray(nBreaks);
- recycleDescents = env->NewFloatArray(nBreaks);
- recycleFlags = env->NewIntArray(nBreaks);
-
- env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
- env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
- env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
- env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
- env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
- }
- // copy data
- env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data());
- env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data());
- env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data());
- env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data());
- env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data());
-}
-
-static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
+static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
// Inputs
jcharArray javaText,
jlong measuredTextPtr,
@@ -122,18 +84,7 @@
jfloat restWidth,
jintArray variableTabStops,
jint defaultTabStop,
- jint indentsOffset,
-
- // Outputs
- jobject recycle,
- jint recycleLength,
- jintArray recycleBreaks,
- jfloatArray recycleWidths,
- jfloatArray recycleAscents,
- jfloatArray recycleDescents,
- jintArray recycleFlags,
- jfloatArray charWidths) {
-
+ jint indentsOffset) {
minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
ScopedCharArrayRO text(env, javaText);
@@ -141,14 +92,44 @@
minikin::U16StringPiece u16Text(text.get(), length);
minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
- minikin::LineBreakResult result = builder->computeBreaks(
- u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
- tabStops.get(), tabStops.size(), defaultTabStop);
- recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
- recycleFlags, recycleLength, result);
+ std::unique_ptr<minikin::LineBreakResult> result =
+ std::make_unique<minikin::LineBreakResult>(builder->computeBreaks(
+ u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
+ tabStops.get(), tabStops.size(), defaultTabStop));
+ return reinterpret_cast<jlong>(result.release());
+}
- return static_cast<jint>(result.breakPoints.size());
+static jint nGetLineCount(jlong ptr) {
+ return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size();
+}
+
+static jint nGetLineBreakOffset(jlong ptr, jint i) {
+ return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i];
+}
+
+static jfloat nGetLineWidth(jlong ptr, jint i) {
+ return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i];
+}
+
+static jfloat nGetLineAscent(jlong ptr, jint i) {
+ return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i];
+}
+
+static jfloat nGetLineDescent(jlong ptr, jint i) {
+ return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i];
+}
+
+static jint nGetLineFlag(jlong ptr, jint i) {
+ return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i];
+}
+
+static void nReleaseResult(jlong ptr) {
+ delete reinterpret_cast<minikin::LineBreakResult*>(ptr);
+}
+
+static jlong nGetReleaseResultFunc() {
+ return reinterpret_cast<jlong>(nReleaseResult);
}
static const JNINativeMethod gMethods[] = {
@@ -166,8 +147,6 @@
// Regular JNI
{"nComputeLineBreaks", "("
"J" // nativePtr
-
- // Inputs
"[C" // text
"J" // MeasuredParagraph ptr.
"I" // length
@@ -177,31 +156,20 @@
"[I" // variableTabStops
"I" // defaultTabStop
"I" // indentsOffset
+ ")J", (void*) nComputeLineBreaks},
- // Outputs
- "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle
- "I" // recycleLength
- "[I" // recycleBreaks
- "[F" // recycleWidths
- "[F" // recycleAscents
- "[F" // recycleDescents
- "[I" // recycleFlags
- ")I", (void*) nComputeLineBreaks}
+ // Result accessors, CriticalNatives
+ {"nGetLineCount", "(J)I", (void*)nGetLineCount},
+ {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset},
+ {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth},
+ {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent},
+ {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent},
+ {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag},
+ {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
};
-int register_android_text_LineBreaker(JNIEnv* env)
-{
- gLineBreaks_class = MakeGlobalRefOrDie(env,
- FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks"));
-
- gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
- gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
- gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
- gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
- gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
-
- return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker",
- gMethods, NELEM(gMethods));
+int register_android_text_LineBreaker(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods));
}
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 46b19bd..63b0046 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -444,6 +444,14 @@
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight();
}
+static jboolean android_view_RenderNode_setAllowForceDark(jlong renderNodePtr, jboolean allow) {
+ return SET_AND_DIRTY(setAllowForceDark, allow, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_getAllowForceDark(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - Animations
// ----------------------------------------------------------------------------
@@ -465,8 +473,8 @@
// SurfaceView position callback
// ----------------------------------------------------------------------------
-jmethodID gSurfaceViewPositionUpdateMethod;
-jmethodID gSurfaceViewPositionLostMethod;
+jmethodID gPositionListener_PositionChangedMethod;
+jmethodID gPositionListener_PositionLostMethod;
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
jlong renderNodePtr, jobject surfaceview) {
@@ -523,7 +531,7 @@
return;
}
- env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod,
+ env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
info ? info->canvasContext.getFrameNumber() : 0);
env->DeleteLocalRef(localref);
}
@@ -547,7 +555,7 @@
env->DeleteWeakGlobalRef(mWeakRef);
mWeakRef = nullptr;
} else {
- env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
+ env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod,
frameNumber, left, top, right, bottom);
env->DeleteLocalRef(localref);
}
@@ -580,7 +588,7 @@
{ "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize },
{ "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
{ "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators },
- { "nRequestPositionUpdates", "(JLandroid/view/SurfaceView;)V", (void*) android_view_RenderNode_requestPositionUpdates },
+ { "nRequestPositionUpdates", "(JLandroid/view/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
{ "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList },
@@ -664,14 +672,16 @@
{ "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
{ "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth },
{ "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
+ { "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
+ { "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark },
};
int register_android_view_RenderNode(JNIEnv* env) {
- jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
- gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
- "updateSurfacePosition_renderWorker", "(JIIII)V");
- gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
- "surfacePositionLost_uiRtSync", "(J)V");
+ jclass clazz = FindClassOrDie(env, "android/view/RenderNode$PositionUpdateListener");
+ gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
+ "positionChanged", "(JIIII)V");
+ gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
+ "positionLost", "(J)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 743b9f6..b70177f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -157,23 +157,17 @@
return Rect(left, top, right, bottom);
}
-static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz,
+static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform,
- int rotation) {
+ bool useIdentityTransform, int rotation) {
sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
if (displayToken == NULL) {
return NULL;
}
Rect sourceCrop = rectFromObj(env, sourceCropObj);
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
sp<GraphicBuffer> buffer;
- status_t res = ScreenshotClient::capture(displayToken,
- sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform,
- rotation, &buffer);
+ status_t res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
+ useIdentityTransform, rotation, &buffer);
if (res != NO_ERROR) {
return NULL;
}
@@ -187,100 +181,6 @@
(jlong)buffer.get());
}
-static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
- jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform,
- int rotation) {
- sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
- if (displayToken == NULL) {
- return NULL;
- }
-
- Rect sourceCrop = rectFromObj(env, sourceCropObj);
-
- std::unique_ptr<ScreenshotClient> screenshot(new ScreenshotClient());
- status_t res;
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
-
- sp<GraphicBuffer> buffer;
- res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer);
- if (res != NO_ERROR) {
- return NULL;
- }
-
- SkColorType colorType;
- SkAlphaType alphaType;
-
- PixelFormat format = buffer->getPixelFormat();
- switch (format) {
- case PIXEL_FORMAT_RGBX_8888: {
- colorType = kRGBA_8888_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
- }
- case PIXEL_FORMAT_RGBA_8888: {
- colorType = kRGBA_8888_SkColorType;
- alphaType = kPremul_SkAlphaType;
- break;
- }
- case PIXEL_FORMAT_RGBA_FP16: {
- colorType = kRGBA_F16_SkColorType;
- alphaType = kPremul_SkAlphaType;
- break;
- }
- case PIXEL_FORMAT_RGB_565: {
- colorType = kRGB_565_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
- }
- default: {
- return NULL;
- }
- }
-
- SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
- colorType, alphaType,
- SkColorSpace::MakeSRGB());
-
- auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
- return bitmap::createBitmap(env, bitmap.release(),
- android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
-}
-
-static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
- jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
- sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
- if (displayToken == NULL) {
- return;
- }
-
- sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
- if (consumer == NULL) {
- return;
- }
-
- Rect sourceCrop;
- if (sourceCropObj != NULL) {
- sourceCrop = rectFromObj(env, sourceCropObj);
- }
-
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
-
- sp<GraphicBuffer> buffer;
- ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer,
- useIdentityTransform, 0, &buffer);
-
- Surface::attachAndQueueBuffer(consumer.get(), buffer);
-}
-
static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
jobject sourceCropObj, jfloat frameScale) {
@@ -919,10 +819,6 @@
(void*)nativeDestroy },
{"nativeDisconnect", "(J)V",
(void*)nativeDisconnect },
- {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/Bitmap;",
- (void*)nativeScreenshotBitmap },
- {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V",
- (void*)nativeScreenshot },
{"nativeCreateTransaction", "()J",
(void*)nativeCreateTransaction },
{"nativeApplyTransaction", "(JZ)V",
@@ -1013,9 +909,8 @@
(void*)nativeDestroyInTransaction },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
(void*)nativeGetHandle },
- {"nativeScreenshotToBuffer",
- "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;",
- (void*)nativeScreenshotToBuffer },
+ {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
+ (void*)nativeScreenshot },
{"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
(void*)nativeCaptureLayers },
};
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3c59bd1..7a5b604 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1064,6 +1064,12 @@
proxy->allocateBuffers(surface);
}
+static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jboolean enable) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->setForceDark(enable);
+}
+
// ----------------------------------------------------------------------------
// FrameMetricsObserver
// ----------------------------------------------------------------------------
@@ -1177,6 +1183,7 @@
{ "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess },
{ "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
{ "nAllocateBuffers", "(JLandroid/view/Surface;)V", (void*)android_view_ThreadedRenderer_allocateBuffers },
+ { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark },
};
static JavaVM* mJvm = nullptr;
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index c15b7ee..109e65c 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -285,10 +285,6 @@
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
jboolean useBpfStats) {
- ScopedUtfChars path8(env, path);
- if (path8.c_str() == NULL) {
- return -1;
- }
std::vector<std::string> limitIfaces;
if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
@@ -308,6 +304,11 @@
if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
return -1;
} else {
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str());
+ return -1;
+ }
if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
limitUid, path8.c_str()) < 0)
return -1;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 364393e..1f95862 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -382,11 +382,10 @@
return 0;
}
-static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& pkg_sandbox_dir,
- std::string* error_msg) {
+static bool createPkgSandbox(uid_t uid, const std::string& package_name, std::string* error_msg) {
// Create /mnt/user/0/package/<package-name>
userid_t user_id = multiuser_get_user_id(uid);
- StringAppendF(&pkg_sandbox_dir, "/%d", user_id);
+ std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) {
*error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
return false;
@@ -396,7 +395,7 @@
*error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
return false;
}
- StringAppendF(&pkg_sandbox_dir, "/%s", package_name);
+ StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
*error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
return false;
@@ -404,10 +403,51 @@
return true;
}
+static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
+ const std::string& mntTargetRoot, const std::string& packageName,
+ const char* dirName, std::string* error_msg) {
+ std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
+ mntSourceRoot.c_str(), dirName, packageName.c_str());
+ std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
+ mntTargetRoot.c_str(), dirName, packageName.c_str());
+ if (TEMP_FAILURE_RETRY(mount(mntSourceDir.c_str(), mntTargetDir.c_str(),
+ nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
+ mntSourceDir.c_str(), mntTargetDir.c_str(), strerror(errno));
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(nullptr, mntTargetDir.c_str(),
+ nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", mntTargetDir.c_str());
+ return false;
+ }
+ return true;
+}
+
+static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
+ const std::vector<std::string>& volumeLabels, userid_t userId, std::string* error_msg) {
+ for (auto& label : volumeLabels) {
+ std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
+ std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
+ if (label == "emulated") {
+ StringAppendF(&mntSource, "/%d", userId);
+ StringAppendF(&mntTarget, "/%d", userId);
+ }
+ for (auto& package : packageNames) {
+ mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
+ mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
+ mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+ }
+ }
+ return true;
+}
+
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
- bool force_mount_namespace, std::string* error_msg, const char* package_name) {
+ bool force_mount_namespace, std::string* error_msg, const std::string& package_name,
+ const std::vector<std::string>& packages_for_uid,
+ const std::vector<std::string>& visible_vol_ids) {
// See storage config details at http://source.android.com/tech/storage/
String8 storageSource;
@@ -459,12 +499,25 @@
return false;
}
} else {
- if (package_name == nullptr) {
+ if (package_name.empty()) {
return true;
}
- std::string pkgSandboxDir("/mnt/user");
- if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
- return false;
+ userid_t user_id = multiuser_get_user_id(uid);
+ std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package/%s",
+ user_id, package_name.c_str());
+ struct stat sb;
+ bool sandboxAlreadyCreated = true;
+ if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
+ if (errno == ENOENT) {
+ ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
+ sandboxAlreadyCreated = false;
+ if (!createPkgSandbox(uid, package_name, error_msg)) {
+ return false;
+ }
+ } else {
+ ALOGE("Failed to lstat %s", pkgSandboxDir.c_str());
+ return false;
+ }
}
if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
@@ -472,6 +525,15 @@
pkgSandboxDir.c_str(), strerror(errno));
return false;
}
+ // If the sandbox was already created by vold, only then set up the bind mounts for
+ // pkg specific directories. Otherwise, leave as is and bind mounts will be taken
+ // care of by vold later.
+ if (sandboxAlreadyCreated) {
+ if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+ user_id, error_msg)) {
+ return false;
+ }
+ }
}
} else {
if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
@@ -611,7 +673,8 @@
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external, jstring java_se_info, jstring java_se_name,
bool is_system_server, bool is_child_zygote, jstring instructionSet,
- jstring dataDir, jstring packageName) {
+ jstring dataDir, jstring packageName, jobjectArray packagesForUid,
+ jobjectArray visibleVolIds) {
std::string error_msg;
auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
@@ -661,17 +724,33 @@
ALOGW("Native bridge will not be used because dataDir == NULL.");
}
- ScopedUtfChars* package_name = nullptr;
- const char* package_name_c_str = nullptr;
+ std::string package_name_str("");
if (packageName != nullptr) {
- package_name = new ScopedUtfChars(env, packageName);
- package_name_c_str = package_name->c_str();
+ ScopedUtfChars package(env, packageName);
+ package_name_str = package.c_str();
} else if (is_system_server) {
- package_name_c_str = "android";
+ package_name_str = "android";
+ }
+ std::vector<std::string> packages_for_uid;
+ if (packagesForUid != nullptr) {
+ jsize count = env->GetArrayLength(packagesForUid);
+ for (jsize i = 0; i < count; ++i) {
+ jstring package_for_uid = (jstring) env->GetObjectArrayElement(packagesForUid, i);
+ ScopedUtfChars package(env, package_for_uid);
+ packages_for_uid.push_back(package.c_str());
+ }
+ }
+ std::vector<std::string> visible_vol_ids;
+ if (visibleVolIds != nullptr) {
+ jsize count = env->GetArrayLength(visibleVolIds);
+ for (jsize i = 0; i < count; ++i) {
+ jstring visible_vol_id = (jstring) env->GetObjectArrayElement(visibleVolIds, i);
+ ScopedUtfChars vol(env, visible_vol_id);
+ visible_vol_ids.push_back(vol.c_str());
+ }
}
bool success = MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
- package_name_c_str);
- delete package_name;
+ package_name_str, packages_for_uid, visible_vol_ids);
if (!success) {
ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
if (errno == ENOTCONN || errno == EROFS) {
@@ -936,7 +1015,8 @@
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
- jstring instructionSet, jstring appDataDir, jstring packageName) {
+ jstring instructionSet, jstring appDataDir, jstring packageName,
+ jobjectArray packagesForUid, jobjectArray visibleVolIds) {
jlong capabilities = 0;
// Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -989,7 +1069,8 @@
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, se_name, false,
- is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName);
+ is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName,
+ packagesForUid, visibleVolIds);
}
return pid;
}
@@ -1003,7 +1084,7 @@
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
- false, NULL, NULL, nullptr);
+ false, NULL, NULL, nullptr, nullptr, nullptr);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -1084,7 +1165,7 @@
{ "nativeSecurityInit", "()V",
(void *) com_android_internal_os_Zygote_nativeSecurityInit },
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index f9f725a..14ed9e6 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -397,6 +397,9 @@
// Ordered GPU debug layer list
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ // App will load ANGLE instead of native GLES drivers.
+ optional SettingProto angle_enabled_app = 3;
}
optional Gpu gpu = 59;
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 9d5f0bc..ab50ad1 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -130,6 +130,13 @@
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional bool keyguard_showing = 1;
+ repeated KeyguardOccludedProto keyguard_occluded_states= 2;
+}
+
+message KeyguardOccludedProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 display_id = 1;
optional bool keyguard_occluded = 2;
}
diff --git a/core/proto/android/server/backup_chunks_metadata.proto b/core/proto/android/server/backup_chunks_metadata.proto
new file mode 100644
index 0000000..a375f02
--- /dev/null
+++ b/core/proto/android/server/backup_chunks_metadata.proto
@@ -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
+ */
+
+syntax = "proto2";
+package com.android.server.backup.encryption.chunk;
+
+option java_outer_classname = "ChunksMetadataProto";
+
+// Cipher type with which the chunks are encrypted. For now we only support AES/GCM/NoPadding, but
+// this is for backwards-compatibility in case we need to change the default Cipher in the future.
+enum CipherType {
+ UNKNOWN_CIPHER_TYPE = 0;
+ // Chunk is prefixed with a 12-byte nonce. The tag length is 16 bytes.
+ AES_256_GCM = 1;
+}
+
+// Checksum type with which the plaintext is verified.
+enum ChecksumType {
+ UNKNOWN_CHECKSUM_TYPE = 0;
+ SHA_256 = 1;
+}
+
+enum ChunkOrderingType {
+ CHUNK_ORDERING_TYPE_UNSPECIFIED = 0;
+ // The chunk ordering contains a list of the start position of each chunk in the encrypted file,
+ // ordered as in the plaintext file. This allows us to recreate the original plaintext file
+ // during decryption. We use this mode for full backups where the order of the data in the file
+ // is important.
+ EXPLICIT_STARTS = 1;
+ // The chunk ordering does not contain any start positions, and instead each encrypted chunk in
+ // the backup file is prefixed with its length. This allows us to decrypt each chunk but does
+ // not give any information about the order. However, we use this mode for key value backups
+ // where the order does not matter.
+ INLINE_LENGTHS = 2;
+}
+
+// Chunk entry (for local state)
+message Chunk {
+ // SHA-256 MAC of the plaintext of the chunk
+ optional bytes hash = 1;
+ // Number of bytes in encrypted chunk
+ optional int32 length = 2;
+}
+
+// List of the chunks in the blob, along with the length of each chunk. From this is it possible to
+// extract individual chunks. (i.e., start position is equal to the sum of the lengths of all
+// preceding chunks.)
+//
+// This is local state stored on the device. It is never sent to the backup server. See
+// ChunkOrdering for how the device restores the chunks in the correct order.
+// Next tag : 6
+message ChunkListing {
+ repeated Chunk chunks = 1;
+
+ // Cipher algorithm with which the chunks are encrypted.
+ optional CipherType cipher_type = 2;
+
+ // Defines the type of chunk order used to encode the backup file on the server, so that we can
+ // consistently use the same type between backups. If unspecified this backup file was created
+ // before INLINE_LENGTHS was supported, thus assume it is EXPLICIT_STARTS.
+ optional ChunkOrderingType chunk_ordering_type = 5;
+
+ // The document ID returned from Scotty server after uploading the blob associated with this
+ // listing. This needs to be sent when uploading new diff scripts.
+ optional string document_id = 3;
+
+ // Fingerprint mixer salt used for content defined chunking. This is randomly generated for each
+ // package during the initial non-incremental backup and reused for incremental backups.
+ optional bytes fingerprint_mixer_salt = 4;
+}
+
+// Ordering information about plaintext and checksum. This is used on restore to reconstruct the
+// blob in its correct order. (The chunk order is randomized so as to give the server less
+// information about which parts of the backup are changing over time.) This proto is encrypted
+// before being uploaded to the server, with a key unknown to the server.
+message ChunkOrdering {
+ // For backups where ChunksMetadata#chunk_ordering_type = EXPLICIT STARTS:
+ // Ordered start positions of chunks. i.e., the file is the chunk starting at this position,
+ // followed by the chunk starting at this position, followed by ... etc. You can compute the
+ // lengths of the chunks by sorting this list then looking at the start position of the next
+ // chunk after the chunk you care about. This is guaranteed to work as all chunks are
+ // represented in this list.
+ //
+ // For backups where ChunksMetadata#chunk_ordering_type = INLINE_LENGTHS:
+ // This field is unused. See ChunkOrderingType#INLINE_LENGTHS.
+ repeated int32 starts = 1 [packed = true];
+
+ // Checksum of plaintext content. (i.e., in correct order.)
+ //
+ // Each chunk also has a MAC, as generated by GCM, so this is NOT Mac-then-Encrypt, which has
+ // security implications. This is an additional checksum to verify that once the chunks have
+ // been reordered, that the file matches the expected plaintext. This prevents the device
+ // restoring garbage data in case of a mismatch between the ChunkOrdering and the backup blob.
+ optional bytes checksum = 2;
+}
+
+// Additional metadata about a backup blob that needs to be synced to the server. This is used on
+// restore to reconstruct the blob in its correct order. (The chunk order is randomized so as to
+// give the server less information about which parts of the backup are changing over time.) This
+// data structure is only ever uploaded to the server encrypted with a key unknown to the server.
+// Next tag : 6
+message ChunksMetadata {
+ // Cipher algorithm with which the chunk listing and chunks are encrypted.
+ optional CipherType cipher_type = 1;
+
+ // Defines the type of chunk order this metadata contains. If unspecified this backup file was
+ // created before INLINE_LENGTHS was supported, thus assume it is EXPLICIT_STARTS.
+ optional ChunkOrderingType chunk_ordering_type = 5
+ [default = CHUNK_ORDERING_TYPE_UNSPECIFIED];
+
+ // Encrypted bytes of ChunkOrdering
+ optional bytes chunk_ordering = 2;
+
+ // The type of algorithm used for the checksum of the plaintext. (See ChunkOrdering.) This is
+ // for forwards compatibility in case we change the algorithm in the future. For now, always
+ // SHA-256.
+ optional ChecksumType checksum_type = 3;
+
+ // This used to be the plaintext tertiary key. No longer used.
+ reserved 4;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
new file mode 100644
index 0000000..941c81f
--- /dev/null
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.usage;
+import "frameworks/base/core/proto/android/content/configuration.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message IntervalStatsProto {
+ message StringPool {
+ optional int32 size = 1;
+ repeated string strings = 2;
+ }
+
+ message CountAndTime {
+ optional int32 count = 1;
+ optional int64 time_ms = 2;
+ }
+
+ // Stores the relevant information from a UsageStats
+ message UsageStats {
+ message ChooserAction {
+ message CategoryCount {
+ optional string name = 1;
+ optional int32 count = 3;
+ }
+ optional string name = 1;
+ repeated CategoryCount counts = 3;
+ }
+ optional string package = 1;
+ // package_index contains the index + 1 of the package name in the string pool
+ optional int32 package_index = 2;
+ optional int64 last_time_active_ms = 3;
+ optional int64 total_time_active_ms = 4;
+ optional int32 last_event = 5;
+ optional int32 app_launch_count = 6;
+ repeated ChooserAction chooser_actions = 7;
+ }
+
+ // Stores the relevant information an IntervalStats will have about a Configuration
+ message Configuration {
+ optional .android.content.ConfigurationProto config = 1;
+ optional int64 last_time_active_ms = 2;
+ optional int64 total_time_active_ms = 3;
+ optional int32 count = 4;
+ optional bool active = 5;
+ }
+
+ // Stores the relevant information from a UsageEvents.Event
+ message Event {
+ optional string package = 1;
+ // package_index contains the index + 1 of the package name in the string pool
+ optional int32 package_index = 2;
+ optional string class = 3;
+ // class_index contains the index + 1 of the class name in the string pool
+ optional int32 class_index = 4;
+ optional int64 time_ms = 5;
+ optional int32 flags = 6;
+ optional int32 type = 7;
+ optional .android.content.ConfigurationProto config = 8;
+ optional string shortcut_id = 9;
+ optional int32 standby_bucket = 11;
+ optional string notification_channel = 12;
+ // notification_channel_index contains the index + 1 of the channel name in the string pool
+ optional int32 notification_channel_index = 13;
+ }
+
+ // The following fields contain supplemental data used to build IntervalStats, such as a string
+ // pool.
+ optional int64 end_time_ms = 1;
+ // stringpool contains all the package and class names used by UsageStats and Event
+ // They will hold a number that is equal to the index + 1 of their string in the pool
+ optional StringPool stringpool = 2;
+
+ // The following fields contain aggregated usage stats data
+ optional CountAndTime interactive = 10;
+ optional CountAndTime non_interactive = 11;
+ optional CountAndTime keyguard_shown = 12;
+ optional CountAndTime keyguard_hidden = 13;
+
+ // The following fields contain listed usage stats data
+ repeated UsageStats packages = 20;
+ repeated Configuration configurations = 21;
+ repeated Event event_log = 22;
+}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 1257336..d33ea0c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -157,6 +157,7 @@
optional int32 rotation = 11;
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
+ optional int32 surface_size = 14;
}
/* represents DisplayFrames */
@@ -211,6 +212,8 @@
optional .android.graphics.RectProto bounds = 5;
optional .android.graphics.RectProto temp_inset_bounds = 6;
optional bool defer_removal = 7;
+ optional int32 surface_width = 8;
+ optional int32 surface_height = 9;
}
/* represents AppWindowToken */
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 8836c2e..c08d7ca 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -201,6 +201,9 @@
optional ConditionProto condition = 9;
optional android.content.ComponentNameProto component = 10;
optional ZenPolicyProto zenPolicy = 11;
+
+ // Indicates whether this ZenRule has been modified after its initial creation
+ optional bool modified = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
// A dump from com.android.server.notification.ZenModeHelper.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1df3f7f..f654ce2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -615,6 +615,8 @@
<protected-broadcast android:name="com.android.server.jobscheduler.FORCE_IDLE" />
<protected-broadcast android:name="com.android.server.jobscheduler.UNFORCE_IDLE" />
+ <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -939,7 +941,6 @@
Requesting this by itself is not sufficient to give you
location access.
<p>Protection level: dangerous
- @hide
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
android:permissionGroup="android.permission-group.LOCATION"
@@ -1184,9 +1185,9 @@
android:priority="700" />
<!-- Required to be able to access the camera device.
- <p>This will automatically enforce the <a
- href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
- <uses-feature>}</a> manifest element for <em>all</em> camera features.
+ <p>This will automatically enforce the
+ <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ uses-feature</a> manifest element for <em>all</em> camera features.
If you do not require all camera features or can properly operate if a camera
is not available, then you must modify your manifest as appropriate in order to
install on devices that don't support all camera features.</p>
@@ -3405,6 +3406,12 @@
<permission android:name="android.permission.DEVICE_POWER"
android:protectionLevel="signature" />
+ <!-- Allows toggling battery saver on the system.
+ Superseded by DEVICE_POWER permission. @hide @SystemApi
+ -->
+ <permission android:name="android.permission.POWER_SAVER"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows access to the PowerManager.userActivity function.
<p>Not for use by third-party applications. @hide @SystemApi -->
<permission android:name="android.permission.USER_ACTIVITY"
@@ -4136,6 +4143,21 @@
<permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
android:protectionLevel="signature" />
+ <!-- @hide Permission that protects the
+ {@link android.provider.Telephony.Intents#ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ broadcast -->
+ <permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE"
+ android:protectionLevel="signature" />
+
+ <!-- A subclass of {@link android.app.SmsAppService} must be protected with this permission. -->
+ <permission android:name="android.permission.BIND_SMS_APP_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Permission that allows background clipboard access.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -4451,14 +4473,6 @@
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
- <service android:name="com.android.internal.backup.LocalTransportService"
- android:permission="android.permission.CONFIRM_FULL_BACKUP"
- android:exported="false">
- <intent-filter>
- <action android:name="android.backup.TRANSPORT_HOST" />
- </intent-filter>
- </service>
-
<service android:name="com.android.server.MountServiceIdler"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" >
diff --git a/core/res/res/drawable/ic_ab_back_material_settings.xml b/core/res/res/drawable/ic_ab_back_material_settings.xml
index 7325a41..938e36d 100644
--- a/core/res/res/drawable/ic_ab_back_material_settings.xml
+++ b/core/res/res/drawable/ic_ab_back_material_settings.xml
@@ -18,11 +18,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
android:autoMirrored="true"
- android:tint="?attr/colorControlNormal">
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
<path
android:fillColor="@color/white"
- android:pathData="M20,11H7.62l4.88,-4.88a0.996,0.996 0,1 0,-1.41 -1.41l-6.94,6.94c-0.2,0.2 -0.2,0.51 0,0.71l6.94,6.94a0.996,0.996 0,1 0,1.41 -1.41L7.62,13H20c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/>
+ android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8l8,8l1.41,-1.41L7.83,13H20V11z"/>
</vector>
+
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c2c9254..5028150 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Probleem om Wi-Fi-oproepe by jou diensverskaffer te registreer: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi-oproep"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g>-Wi-Fi-oproepe"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-oproep"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g>-WLAN-oproep"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Vingerafdrukhardeware is nie beskikbaar nie."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan nie gestoor word nie. Verwyder asseblief \'n bestaande vingerafdruk."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Gesiguittelling is bereik. Probeer weer."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Gesig kan nie geberg word nie."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Gesighandeling is gekanselleer."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Gesigstawing is deur gebruiker gekanselleer."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogings. Probeer later weer."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Te veel pogings. Gesigstawingsensor is gedeaktiveer."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Probeer weer."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurkorreksie"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Toeganklikheidskortpad het <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Toeganklikheidskortpad het <xliff:g id="SERVICE_NAME">%1$s</xliff:g> afgeskakel"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gebruik weer toeganklikheidskortpad om die huidige toeganklikheidskenmerk te begin"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Kies \'n kenmerk om te gebruik wanneer jy op die Toeganklikheid-knoppie tik:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Raak en hou die Toeganklikheid-knoppie om kenmerke te verander."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Vergroting"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 45f962b..217d631 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"ከእርስዎ አገልግሎት አቅራቢ ጋር የWi‑Fi ጥሪን በማስመዝገብ ላይ ችግር፦ <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"የ%s Wi-Fi ጥሪ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> የWi-Fi ጥሪ አደራረግ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"የWLAN ጥሪ"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> የWLAN ጥሪ"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"የጣት አሻራ ሃርድዌር አይገኝም።"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"የጣት አሻራ ሊከማች አይችልም። እባክዎ አሁን ያለውን የጣት አሻራ ያስወግዱ።"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"የፊት ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ፊት ሊከማች አይችልም።"</string>
<string name="face_error_canceled" msgid="283945501061931023">"የፊት ሥርዓተ ክወና ተሰርዟል።"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ፊትን ማረጋገጥ በተጠቃሚ ተሰርዟል።"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"በጣም ብዙ ሙከራዎች። የፊት ማረጋገጫ ተሰናክሏል።"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"እንደገና ይሞክሩ።"</string>
@@ -868,7 +875,7 @@
<string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"በፊት መክፈት።"</string>
<string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"በፒን መክፈት።"</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="9149698847116962307">"የሲም ፒን ክፈት።"</string>
- <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"የሲም ፒዩኬ ክፈት።"</string>
+ <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"የሲም PUK ክፈት።"</string>
<string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"በይለፍ ቃል መክፈት።"</string>
<string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"የስርዓተ-ጥለት አካባቢ።"</string>
<string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"የማንሸራተቻ አካባቢ።"</string>
@@ -1582,7 +1589,7 @@
<string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"ሲም ካርዱን በመክፈት ላይ…"</string>
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"ትክክል ያልሆነ ፒን ኮድ።"</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ከ4 እስከ 8 ቁጥሮች የያዘ ፒን ይተይቡ።"</string>
- <string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"የፒዩኬ ኮድ 8 ቁጥሮች ነው መሆን ያለበት።"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"የPUK ኮድ 8 ቁጥሮች ነው መሆን ያለበት።"</string>
<string name="kg_invalid_puk" msgid="3638289409676051243">"ትክክለኛውን የPUK ኮድ እንደገና ያስገቡ። ተደጋጋሚ ሙከራዎች ሲም ካርዱን እስከመጨረሻው ያሰናክሉታል።"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"ፒን ኮዶች አይገጣጠሙም"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"በጣም ብዙ የስርዓተ ጥለት ሙከራዎች"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"የቀለም ማስተካከያ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"የተደራሽነት አቋራጭ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን አብርቶታል"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"የተደራሽነት አቋራጭ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን አጥፍቶታል"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"አሁን ያለውን የተደራሽነት ባህሪ ለመጀመር እንደገና የተደራሽነት አቋራጭን ይጠቀሙ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"የተደራሽነት አዝራርን መታ በሚያደርጉበት ጊዜ ጥቅም ላይ የሚውለውን ባህሪ ይምረጡ፦"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ባህሪያትን ለመለወጥ የተደራሽነት አዝራሩን ይንኩ እና ይያዙት።"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ማጉላት"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index bbdea6a..c11279a 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -132,10 +132,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"حدثت مشكلة أثناء تسجيل الاتصال عبر Wi‑Fi باستخدام مشغِّل شبكة الجوّال: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s الاتصال عبر Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"الاتصال عبر شبكة Wi-Fi التابعة لـ <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"مكالمة عبر شبكة WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"مكالمة عبر شبكة WLAN التابعة لـ <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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 +540,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"جهاز بصمة الإصبع غير متاح."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"يتعذر تخزين بصمة الإصبع؛ يرجى إزالة إحدى البصمات المخزنة."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"تم بلوغ مهلة إدخال بصمة الإصبع. أعد المحاولة."</string>
@@ -572,6 +578,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"انتهت مهلة التعرُّف على الوجه. أعِد المحاولة."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"يتعذَّر حفظ الوجه."</string>
<string name="face_error_canceled" msgid="283945501061931023">"تمّ إلغاء عملية مصادقة الوجه."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ألغَى المستخدم مصادقة الوجه."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"تمّ إجراء محاولات كثيرة. أعِد المحاولة لاحقًا."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"تمّ إجراء محاولات كثيرة. ميزة مصادقة الوجه متوقفة."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"يُرجى إعادة المحاولة."</string>
@@ -1712,6 +1719,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"تصحيح الألوان"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"شغَّل اختصار إمكانية الوصول خدمة <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"أوقف اختصار إمكانية الوصول خدمة <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"استخدِم اختصار إمكانية الوصول لبدء ميزة إمكانية الوصول الحالية."</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"يمكنك اختيار إحدى الميزات لاستخدامها عند النقر على زر إمكانية الوصول:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"لتغيير الميزات، يمكنك لمس زر \"إمكانية الوصول\" مع الاستمرار."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"التكبير"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 48d44b0..5a0a7ec 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"আপোনাৰ বাহকৰ ওচৰত ৱাই-ফাই কলিং সুবিধা পঞ্জীয়ন কৰাত সমস্যাৰ উদ্ভৱ হৈছে: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s ৱাই- ফাই কলিং"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ৱাই- ফাই কলিং"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN কল"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN কল"</string>
+ <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>
<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>
@@ -308,7 +312,7 @@
<string name="permgrouprequest_visual" msgid="6907523945030290376">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক আপোনাৰ ফট’ আৰু ভিডিঅ’সমূহ এক্সেছ কৰিবলৈ দিবনে?"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ৱিণ্ড\' সমল বিচাৰি উলিয়াওক"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপুনি যোগাযোগ কৰি থকা ৱিণ্ড\'খনৰ সমল পৰীক্ষা কৰক।"</string>
- <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পৰ্শৰদ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰক"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰক"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"টেপ কৰা বস্তুসমূহ ডাঙৰকৈ কোৱা হ\'ব আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ দি স্ক্ৰীণ অন্বেষণ কৰিব পাৰিব।"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপুনি লিখা পাঠ নিৰীক্ষণ কৰক"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্ৰেডিট কাৰ্ডৰ নম্বৰ আৰু পাছৱৰ্ডৰ দৰে ব্যক্তিগত ডেটা অন্তৰ্ভুক্ত হ\'ব পাৰে।"</string>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ফিংগাৰপ্ৰিণ্ট সঞ্চয় কৰিব পৰা নগ\'ল। পূর্বে সঞ্চিত ফিংগাৰপ্ৰিণ্ট এটা আঁতৰাওক।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ফিংগাৰপ্ৰিণ্ট গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"মুখমণ্ডল গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"মুখমণ্ডল সঞ্চয় কৰিব নোৱাৰি।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"মুখমণ্ডলৰ প্ৰক্ৰিয়া বাতিল কৰা হ’ল।"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ব্যৱহাৰকাৰীয়ে মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বাতিল কৰিছে।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"অত্যধিক প্ৰয়াস। মুখমণ্ডলৰ জৰিয়তে সত্যাপন অক্ষম কৰা হ’ল।"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"আকৌ চেষ্টা কৰক।"</string>
@@ -1404,7 +1411,7 @@
<string name="forward_intent_to_work" msgid="621480743856004612">"আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ ভিতৰত এই এপটো ব্যৱহাৰ কৰি আছে"</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"ইনপুট পদ্ধতি"</string>
<string name="sync_binding_label" msgid="3687969138375092423">"ছিংক"</string>
- <string name="accessibility_binding_label" msgid="4148120742096474641">"সাধ্য় সুবিধাসমূহ"</string>
+ <string name="accessibility_binding_label" msgid="4148120742096474641">"সাধ্য সুবিধাসমূহ"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ৱালপেপাৰ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ৱালপেপাৰ সলনি কৰক"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"জাননী নিৰীক্ষক"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ৰং শুধৰণী"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাটটোৱে <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ক অন কৰিছে"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাটটোৱে <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ক অফ কৰিছে"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"বর্তমানৰ সাধ্য সুবিধাসমূহৰ আৰম্ভ কৰিবলৈ সাধ্য সুবিধাসমূহৰ শ্বৰ্টকাট পুনৰ ব্যৱহাৰ কৰক"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"আপুনি দিব্যাংগসকলৰ সুবিধাৰ বুটামটো টিপিলে ব্যৱহাৰ কৰিবলগীয়া কোনো সুবিধা বাছক:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"সুবিধাসমূহ সলনি কৰিবলৈ দিব্যাংগসকলৰ সুবিধাৰ বুটামটো স্পৰ্শ কৰি থাকক।"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"বিবৰ্ধন"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 44bcd56..af65bbb 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Operatorla Wi‑Fi zənglərini qeydə alarkən xəta baş verdi: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Zəngi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Zəngi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN Zəngi"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Zəngi"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Barmaq izi üçün avadanlıq yoxdur."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Barmaq izi saxlana bilməz. Lütfən, mövcud barmaq izini silin."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Barmaq izinin vaxtı başa çatdı. Yenidən cəhd edin."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Üz proqramı taymerinin vaxtı bitdi. Yenidən cəhd edin."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Üz bərpa edilmədi."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Üz əməliyyatı ləğv edildi."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Üz dorğulaması istifadəçi tərəfindən ləğv edildi."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Həddindən çox cəhd. Sonraya saxlayın."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Həddindən çox cəhd. Üz identifikasiyası deaktiv edildi."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Yenidən cəhd edin."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Rəng korreksiyası"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Əlçatımlıq Qısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini aktiv etdi"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Əlçatımlıq Qısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini deaktiv etdi"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Cari əlçatımlılıq funksiyasını yenidən başlatmaq üçün Əlçatımlılıq Qısayolundan istifadə edin"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Əlçatımlılıq düyməsinə kliklədikdə istifadə etmək üçün funksiya seçin:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Funksiyaları dəyişmək üçün Əlçatımlılıq düyməsinə basıb saxlayın."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Böyütmə"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 260f63e..805082a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -129,10 +129,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Problem u vezi sa registrovanjem pozivanja preko Wi‑Fi-ja kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Wi-Fi pozivanje preko operatera %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN poziv"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN poziv"</string>
+ <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>
<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>
@@ -527,6 +531,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otiske prstiju nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Nije moguće sačuvati otisak prsta. Uklonite neki od postojećih otisaka prstiju."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vremensko ograničenje za otisak prsta je isteklo. Probajte ponovo."</string>
@@ -563,6 +569,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Isteklo je vreme za proveru lica. Probajte ponovo."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće sačuvati lice."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Obrada lica je otkazana."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Korisnik je otkazao potvrdu lica."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Probajte ponovo kasnije."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Više pokušaja. Potvrda identiteta je onemogućena."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Probajte ponovo."</string>
@@ -1640,6 +1647,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcija boja"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Upotrebite ponovo prečicu za pristupačnost da biste pokrenuli aktuelnu funkciju pristupačnosti"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izaberite funkciju koja će se koristiti kada dodirnete dugme za pristupačnost:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pritisnite i zadržite dugme za pristupačnost da biste menjali funkcije."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Uvećanje"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 3d46662..7692220 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Памылка падключэння Wi‑Fi-тэлефаніі ў вашага аператара: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Wi-Fi-тэлефанія %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Wi-Fi-тэлефанія ад <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Выклік праз WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Выклік праз WLAN ад <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Апаратныя сродкі адбіткаў пальцаў недаступныя."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Адбіткі пальцаў нельга захаваць. Выдаліце існы адбітак."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час чакання адбіткаў пальцаў выйшаў. Паспрабуйце яшчэ раз."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Час чакання твару выйшаў. Паўтарыце спробу."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Не ўдалося захаваць твар."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Распазнаванне твару скасавана."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Распазнаванне твару скасавана карыстальнікам."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Занадта шмат спроб. Паўтарыце спробу пазней."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Занадта шмат спроб. Аўтэнтыфікацыя твару адключана"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Паўтарыце спробу."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Карэкцыя колеру"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў уключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў адключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Каб запусціць функцыю спецыяльных магчымасцей, паўторна скарыстайце ярлык спецыяльных магчымасцей"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Выберыце функцыю для выкарыстання пры націску кнопкі \"Спецыяльныя магчымасці\":"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Каб змяняць функцыі, краніце і ўтрымлівайце кнопку \"Спецыяльныя магчымасці\"."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Павелічэнне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e9ecba6..33c4898 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"При регистрирането на функцията за обаждания през Wi-Fi с оператора ви възникна грешка: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s – обаждания през Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Обаждания през Wi-Fi от <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Обаждане през WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Обаждане през WLAN от <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардуерът за отпечатъци не е налице."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатъкът не може да бъде съхранен. Моля, премахнете съществуващ."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Времето за изчакване за отпечатък изтече. Опитайте отново."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Времето за изчакване изтече. Опитайте отново."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Лицето не може да бъде съхранено."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Операцията с лице е анулирана."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Удостоверяв. на лицето е анулирано от потребителя."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Твърде много опити. Опитайте отново по-късно."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Твърде много опити. Удост. с лице е деактивирано."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Опитайте отново."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Коригиране на цветовете"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Прекият път за достъпност включи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Прекият път за достъпност изключи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Използвайте отново прекия път към функцията за достъпност, за да стартирате текущата"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изберете функция, която да използвате, когато докоснете бутона за достъпност:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"За да промените функции, докоснете и задръжте бутона за достъпност."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ниво на мащаба"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 4ece21b..f0787b4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"পরিষেবা প্রদানকারীতে ওয়াই-ফাই কলিং রেজিস্টার করতে সমস্যা হয়েছে: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s ওয়াই-ফাই কলিং"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ওয়াই-ফাই কলিং"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN কল"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN কল"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার অনুপলব্ধ৷"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"আঙ্গুলের ছাপ সংরক্ষণ করা যাবে না৷ অনুগ্রহ করে একটি বিদ্যমান আঙ্গুলের ছাপ সরান৷"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ফেসের ছাপ নেওয়ার সময়সীমা শেষ৷ আবার চেষ্টা করুন৷"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ফেস স্টোর করা যাবে না।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ফেস অপারেশন বাতিল করা হয়েছে৷"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ফেস যাচাইকরণ ব্যবহারকারীর দ্বারা বাতিল করা হয়েছে।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"অনেকবার চেষ্টা করা হয়েছে। পরে আবার চেষ্টা করুন।"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"অনেকবার চেষ্টা করা হয়েছে৷ ফেস যাচাইকরণ বন্ধ আছে।"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"আবার চেষ্টা করুন।"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"রঙ সংশোধন"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে চালু করেছে"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে বন্ধ করেছে"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"বর্তমান অ্যাক্সেসিবিলিটি বৈশিষ্ট্য শুরু করার জন্য অ্যাক্সেসিবিলিটি শর্টকাটটি আবার ব্যবহার করুন"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"অ্যাক্সেসযোগ্যতা বোতামের সাহায্যে যে বৈশিষ্ট্যটি নিয়ন্ত্রণ করতে চান, সেটি বেছে নিন:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"বৈশিষ্ট্যগুলি পরিবর্তন করতে অ্যাক্সেসযোগ্যতা বোতামটি ট্যাপ করে ধরে রাখুন।"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"বড় করে দেখা"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 918ecf9..e210d90e 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -129,10 +129,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Došlo je do problema prilikom registracije pozivanja putem WiFi mreže kod vašeg operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Pozivanje putem WiFi-ja preko operatera %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Pozivanje putem WiFi-ja"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN poziv"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN poziv"</string>
+ <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>
<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>
@@ -527,6 +531,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta se ne može pohraniti. Uklonite postojeći otisak prsta."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vrijeme za prepoznavanje otiska prsta je isteklo. Pokušajte ponovo."</string>
@@ -563,6 +569,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Vrijeme za prepoznavanje lica je isteklo. Pokušajte ponovo."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće pohraniti lice."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Prepoznavanje lica je otkazano."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Korisnik je otkazao provjeru lica."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Previše pokušaja. Autentifikacija lica onemogućena."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Pokušajte ponovo."</string>
@@ -1642,6 +1649,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Ispravka boja"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Ponovo koristite Prečicu za pristupačnost da započnete trenutnu funkciju pristupačnosti"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Da promijenite funkcije, dodirnite i držite dugme Pristupačnost."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Uvećanje"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index f6c46ba..b1d4994 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Hi ha hagut un problema en registrar les trucades per Wi-Fi amb el teu operador de telefonia mòbil: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Trucades per Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Trucades per Wi‑Fi (<xliff:g id="SPN">%s</xliff:g>)"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Trucada per WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Trucada per WLAN (<xliff:g id="SPN">%s</xliff:g>)"</string>
+ <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>
<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>
@@ -295,7 +299,7 @@
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"llegir i editar el registre de trucades del telèfon"</string>
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi als registres de trucades del telèfon?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telèfon"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"fer i gestionar trucades telefòniques"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"fer i gestionar trucades"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> faci trucades i les gestioni?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensors corporals"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedir a les dades del sensor sobre els signes vitals"</string>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El maquinari per a empremtes digitals no està disponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"S\'ha esgotat el temps d\'espera. Torna-ho a provar."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"La cara no es pot desar."</string>
<string name="face_error_canceled" msgid="283945501061931023">"S\'ha cancel·lat el reconeixement facial."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autenticació facial cancel·lada per l\'usuari."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Massa intents. Torna-ho a provar més tard."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Massa intents. Autenticació facial desactivada."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Torna-ho a provar."</string>
@@ -804,8 +811,8 @@
<string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"Falta la targeta SIM o no es pot llegir. Insereix-ne una."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Targeta SIM no utilitzable."</string>
<string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"La targeta SIM està desactivada permanentment.\n Contacta amb el teu proveïdor de serveis sense fil per obtenir-ne una altra."</string>
- <string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Ruta anterior"</string>
- <string name="lockscreen_transport_next_description" msgid="573285210424377338">"Ruta següent"</string>
+ <string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Pista anterior"</string>
+ <string name="lockscreen_transport_next_description" msgid="573285210424377338">"Pista següent"</string>
<string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Posa en pausa"</string>
<string name="lockscreen_transport_play_description" msgid="1901258823643886401">"Reprodueix"</string>
<string name="lockscreen_transport_stop_description" msgid="5907083260651210034">"Atura"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correcció del color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"La drecera d\'accessibilitat ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"La drecera d\'accessibilitat ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Torna a utilitzar la drecera d\'accessibilitat per iniciar la funció d\'accessibilitat actual"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Tria la funció que s\'utilitzarà quan toquis el botó Accessibilitat:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Per canviar les funcions, toca i mantén premut el botó Accessibilitat."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliació"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index e882f42..a537d6d 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Při registraci volání přes Wi-Fi u operátora <xliff:g id="CODE">%1$s</xliff:g> došlo k chybě"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Volání přes Wi-Fi: %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g>: volání přes Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Volání přes WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g>: volání přes WLAN"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Není k dispozici hardware ke snímání otisků prstů."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisk prstu nelze uložit. Odstraňte existující otisk prstu."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit sejmutí otisku prstu vypršel. Zkuste to znovu."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Limit ověření obličeje vypršel. Zkuste to znovu."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Obličej nelze uložit."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operace snímání obličeje byla zrušena."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Ověření obličeje bylo zrušeno uživatelem."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Příliš mnoho pokusů. Zkuste to později."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Příliš mnoho pokusů. Ověření obličeje je zakázáno."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Zkuste to znovu."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Oprava barev"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Zkratka přístupnosti zapnula službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Zkratka přístupnosti vypnula službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Znovu použijte zkratku přístupnosti, čímž spustíte aktuální funkci pro usnadnění přístupu"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Určete, jakou funkci aktivujete klepnutím na tlačítko Přístupnost:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Chcete-li vybrat jinou funkci, podržte tlačítko Přístupnost."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zvětšení"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9a793a8..28c08b1 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Der opstod et problem under registrering af Wi-Fi-opkald hos dit mobilselskab: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi-opkald"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Wi-Fi-opkald via <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-opkald"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"WLAN-opkald via <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardwaren til fingeraftryk er ikke tilgængelig."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeraftrykket kan ikke gemmes. Fjern et eksisterende fingeraftryk."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Registrering af fingeraftryk fik timeout. Prøv igen."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Ansigtsgenkendelse fik timeout. Prøv igen."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Ansigtet kan ikke gemmes."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Ansigtshandlingen blev annulleret."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Ansigtsgodkendelsen blev annulleret af brugeren."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Du har prøvet for mange gange. Prøv igen senere."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"For mange forsøg – Ansigtsgenkendelse deaktiveret."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Prøv igen."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Korriger farve"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Genvejen til hjælpefunktioner aktiverede <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Genvejen til hjælpefunktioner deaktiverede <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Brug Genvej til hjælpefunktioner for at starte den aktuelle hjælpefunktion"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Vælg, hvilken funktion du vil bruge, når du trykker på knappen Hjælpefunktioner:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Tryk på knappen Hjælpefunktioner, og hold fingeren nede for at skifte funktioner."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Forstørrelse"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 8eb0eb8..4a2efbd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Probleme beim Registrieren der WLAN-Telefonie bei deinem Mobilfunkanbieter: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s WLAN-Telefonie"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> WLAN-Telefonie"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-Anruf"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN-Anruf"</string>
+ <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>
<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>
@@ -150,7 +154,7 @@
<string name="httpErrorAuth" msgid="1435065629438044534">"Bei der Authentifizierung ist ein Fehler aufgetreten."</string>
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"Authentifizierung via Proxyserver ist fehlgeschlagen."</string>
<string name="httpErrorConnect" msgid="8714273236364640549">"Verbindung zum Server konnte nicht hergestellt werden."</string>
- <string name="httpErrorIO" msgid="2340558197489302188">"Kommunikation mit dem Server konnte nicht hergestellt werden. Bitte versuche es später erneut."</string>
+ <string name="httpErrorIO" msgid="2340558197489302188">"Kommunikation mit dem Server konnte nicht hergestellt werden. Bitte versuche es später noch einmal."</string>
<string name="httpErrorTimeout" msgid="4743403703762883954">"Zeitüberschreitung bei Serververbindung."</string>
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"Die Seite enthält zu viele Server-Redirects."</string>
<string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"Das Protokoll wird nicht unterstützt."</string>
@@ -158,7 +162,7 @@
<string name="httpErrorBadUrl" msgid="3636929722728881972">"Die Seite kann nicht geöffnet werden, weil die URL ungültig ist."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"Auf die Datei konnte nicht zugegriffen werden."</string>
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"Die angeforderte Datei wurde nicht gefunden."</string>
- <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Versuche es später erneut."</string>
+ <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Bitte versuche es später noch einmal."</string>
<string name="notification_title" msgid="8967710025036163822">"Fehler bei Anmeldung für <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchronisierung"</string>
<string name="contentServiceSyncNotificationTitle" msgid="7036196943673524858">"Synchronisierung nicht möglich"</string>
@@ -515,23 +519,25 @@
<string name="permlab_mediaLocation" msgid="8675148183726247864">"Standorte aus meiner Mediensammlung abrufen"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ermöglicht der App, Standorte aus deiner Mediensammlung abzurufen."</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische Hardware nicht verfügbar"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Versuche es erneut."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Versuche es erneut."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es erneut."</string>
- <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt. Versuche es erneut."</string>
- <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger zu langsam bewegt. Versuche es erneut."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_too_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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerabdruckhardware nicht verfügbar"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerabdruck kann nicht gespeichert werden. Entferne einen vorhandenen Fingerabdruck."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Versuche es erneut."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche. Versuche es später erneut."</string>
+ <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche. Bitte versuche es später noch einmal."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Zu viele Versuche. Der Fingerabdrucksensor wurde deaktiviert."</string>
- <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Bitte versuche es erneut."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Keine Fingerabdrücke erfasst."</string>
<string name="fingerprint_error_hw_not_present" msgid="5729436878065119329">"Dieses Gerät hat keinen Fingerabdrucksensor"</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Zeitüberschreitung für Gesicht. Versuch es erneut."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Gesicht kann nicht gespeichert werden."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Gesichtsauthentifizierung vom Nutzer abgebrochen."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche. Versuch es später noch einmal."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zu viele Versuche. Gesichtserkennung deaktiviert."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Versuch es noch einmal."</string>
@@ -1085,7 +1092,7 @@
<string name="view_flight" msgid="7691640491425680214">"Verfolgen"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Ausgewählten Flug verfolgen"</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 möglicherweise nicht."</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>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird ausgeführt"</string>
<string name="app_running_notification_text" msgid="1197581823314971177">"Für weitere Informationen oder zum Beenden der App tippen."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Farbkorrektur"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde durch die Bedienungshilfenverknüpfung aktiviert"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde durch die Bedienungshilfenverknüpfung deaktiviert"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Verwende noch einmal die Verknüpfung für Bedienungshilfen, um die aktuelle Bedienungshilfe zu starten."</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Wähle eine Funktion aus, die verwendet wird, wenn du auf die Schaltfläche für die Bedienungshilfen tippst:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Um die Funktionen zu ändern, halte die Schaltfläche für die Bedienungshilfen gedrückt."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Vergrößerung"</string>
@@ -1723,7 +1731,7 @@
<string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"Neue PIN"</string>
<string name="restr_pin_confirm_pin" msgid="8501523829633146239">"Neue PIN bestätigen"</string>
<string name="restr_pin_create_pin" msgid="8017600000263450337">"PIN für das Ändern von Einschränkungen erstellen"</string>
- <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"Die PINs stimmen nicht überein. Bitte versuche es erneut."</string>
+ <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"Die PINs stimmen nicht überein. Bitte versuche es noch einmal."</string>
<string name="restr_pin_error_too_short" msgid="8173982756265777792">"Die PIN ist zu kurz. Sie muss mindestens 4 Ziffern umfassen."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="9061246974881224688">
<item quantity="other">In <xliff:g id="COUNT">%d</xliff:g> Sek. wiederholen</item>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 3a50f9e..04b9749 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Παρουσιάστηκε πρόβλημα με την εγγραφή της κλήσης Wi‑Fi με την εταιρεία κινητής τηλεφωνίας: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Κλήση Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Κλήση Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Κλήση μέσω WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Κλήση μέσω WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Ο εξοπλισμός μοναδικού χαρακτηριστικού δεν είναι διαθέσιμος."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Δεν είναι δυνατή η αποθήκευση μοναδικού χαρακτηριστικού. Καταργήστε το υπάρχον μοναδικό χαρακτηριστικό."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Λήξη χρονικού ορίου μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Λήξη χρονικού ορίου προσώπου. Δοκιμάστε ξανά."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Δεν είναι δυνατή η αποθήκευση του προσώπου."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Η ενέργεια προσώπου ακυρώθηκε."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Ο έλεγχος ταυτότητας προσώπου ακυρώθηκε από τον χρήστη."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Πολλές προσπάθειες. Αποτυχία ελέγ. ταυτ. προσώπου."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Δοκιμάστε ξανά."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Διόρθωση χρωμάτων"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Η συντόμευση προσβασιμότητας ενεργοποίησε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Η συντόμευση προσβασιμότητας απενεργοποίησε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Χρησιμοποιήστε τη Συντόμευση προσβασιμότητας ξανά, για να ξεκινήσετε την τρέχουσα λειτουργία προσβασιμότητας"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Επιλέξτε μια λειτουργία που θα χρησιμοποιείται κατά το πάτημα του κουμπιού \"Προσβασιμότητα\"."</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Για να αλλάξετε λειτουργίες, αγγίξτε παρατεταμένα το κουμπί \"Προσβασιμότητα\"."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Μεγιστοποίηση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 04b20ba..85923550b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Issue registering Wi‑Fi calling with your operator: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN call"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Call"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Face authentication cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch & hold the Accessibility button."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index c1ca0c4..1e075f8 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Issue registering Wi‑Fi calling with your operator: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN call"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Call"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Face authentication cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch & hold the Accessibility button."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 04b20ba..85923550b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Issue registering Wi‑Fi calling with your operator: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN call"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Call"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Face authentication cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch & hold the Accessibility button."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 04b20ba..85923550b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Issue registering Wi‑Fi calling with your operator: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN call"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Call"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Face authentication cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Colour Correction"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch & hold the Accessibility button."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 4add775..9138098 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Issue registering Wi‑Fi calling with your carrier: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN Call"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Call"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint time out reached. Try again."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation canceled."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Face authentication canceled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Color Correction"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use Accessibility Shortcut again to start the current accessibility feature"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choose a feature to use when you tap the Accessibility button:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"To change features, touch & hold the Accessibility button."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Magnification"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f317a4e..6176b7b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Error al registrar la Llamada con Wi‑Fi con tu operador: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Llamada por Wi-Fi de %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Llamada por Wi-Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Llamada por WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Llamada por WLAN de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El hardware para detectar huellas digitales no está disponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una de las existentes."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Finalizó el tiempo de espera para la huella digital. Vuelve a intentarlo."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Se agotó el tiempo. Vuelve a intentarlo."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"No se puede almacenar el rostro."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Se canceló el reconocimiento facial."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"El usuario canceló la autenticación de rostro."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autent. facial inhabilitada."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Vuelve a intentarlo."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"El acceso directo de accesibilidad activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo de accesibilidad desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Vuelve a usar el acceso directo de accesibilidad para iniciar la función de accesibilidad actual"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Elige una función para usar cuando presionas el botón Accesibilidad:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para cambiar funciones, mantén presionado el botón Accesibilidad."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliación"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c42f53c..dd766f7 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"No se ha podido registrar la llamada por Wi‑Fi con tu operador: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Llamadas Wi-Fi de %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Llamada por Wi‑Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Llamada por WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Llamada por WLAN de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El hardware de huella digital no está disponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una ya creada."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Se ha alcanzado el tiempo de espera de la huella digital. Vuelve a intentarlo."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Has sobrepasado el tiempo. Inténtalo de nuevo."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"No se pueden registrar más caras."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Se ha cancelado el reconocimiento facial."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"El usuario ha cancelado la autenticación de la cara."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autent. facial inhabilitada."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Inténtalo de nuevo."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"El acceso directo a accesibilidad ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo a accesibilidad ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Usa de nuevo la combinación de teclas de accesibilidad para iniciar la función de accesibilidad actual"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Selecciona la función que se utilizará cuando toques el botón Accesibilidad:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para cambiar las funciones, mantén pulsado el botón Accesibilidad."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index cc052c4..650fcb6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Probleem WiFi-kõnede registreerimisel teie operaatoriga: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s WiFi kaudu helistamine"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g>: WiFi-kõned"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-kõne"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g>: WLAN-kõne"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Sõrmejälje riistvara pole saadaval."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sõrmejälge ei saa salvestada. Eemaldage olemasolev sõrmejälg."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Sõrmejälje riistvara taimeri ajalõpp. Proovige uuesti."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Näotuvastuse taimeri ajalõpp. Proovige uuesti."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Nägu ei saa salvestada."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Näotuvastuse toiming tühistati."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Kasutaja tühistas näo autentimise."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Liiga palju katseid. Proovige hiljem uuesti."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Liiga palju katseid. Näotuvastus on keelatud."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Proovige uuesti."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Värviparandus"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> sisse"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> välja"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Praeguse juurdepääsufunktsiooni käivitamiseks kasutage juurdpääsetavuse otseteed uuesti"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Valige, millist funktsiooni kasutada, kui vajutate nuppu Juurdepääsetavus:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Funktsioonide muutmiseks puudutage pikalt nuppu Juurdepääsetavus."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Suurendus"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 275e171..6a6fdbf 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Arazo bat izan da Wi‑Fi bidezko deiak zure operadorearekin erregistratzean: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi bidezko deiak"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi bidezko deiak"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN bidezko deia"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN bidezko deia"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hatz-markaren hardwarea ez dago erabilgarri."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ezin da gorde hatz-marka digitala. Kendu lehendik gordeta duzunetako bat."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Hatz-marka digitalak prozesatzeko denbora-muga gainditu da. Saiatu berriro."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Gainditu da aurpegiak prozesatzeko denbora-muga. Saiatu berriro."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Ezin da gorde aurpegia."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Utzi da aurpegiaren bidezko eragiketa."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Erabiltzaileak utzi du aurpegi-autentifikazioa."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Saiakera gehiegi egin dituzu. Desgaitu egin da autentifikazioa."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Saiatu berriro."</string>
@@ -1129,7 +1136,7 @@
<string name="aerr_application_repeated" msgid="3146328699537439573">"Behin eta berriz gelditzen ari da <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"Behin eta berriz gelditzen ari da <xliff:g id="PROCESS">%1$s</xliff:g>"</string>
<string name="aerr_restart" msgid="7581308074153624475">"Ireki aplikazioa berriro"</string>
- <string name="aerr_report" msgid="5371800241488400617">"Bidali iritzia"</string>
+ <string name="aerr_report" msgid="5371800241488400617">"Bidali oharrak"</string>
<string name="aerr_close" msgid="2991640326563991340">"Itxi"</string>
<string name="aerr_mute" msgid="1974781923723235953">"Ezkutatu gailua berrabiarazi arte"</string>
<string name="aerr_wait" msgid="3199956902437040261">"Itxaron"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Koloreen zuzenketa"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Erabilerraztasun-lasterbideak <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu du"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Erabilerraztasun-lasterbideak <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu du"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Uneko erabilerraztasun-eginbidea abiarazteko, erabili erabilerraztasun-lasterbidea berriro"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoia sakatzean:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Eginbideak aldatzeko, eduki sakatuta Erabilerraztasuna botoia."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Lupa"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 5055e8b..2310f9e 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"مشکل هنگام ثبت تماس ازطریق Wi‑Fi با شرکت مخابراتی: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"تماس %s Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"تماس ازطریق Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"تماس ازطریق WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"تماس ازطریق WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"سختافزار اثرانگشت در دسترس نیست."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"مهلت زمانی شناسایی چهره تمام شد. دوباره امتحان کنید"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"نمیتوان چهره را ذخیره کرد."</string>
<string name="face_error_canceled" msgid="283945501061931023">"عملیات شناسایی چهره لغو شد."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"احراز هویت چهره توسط کاربر لغو شد."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"چندین تلاش ناموفق. احراز هویت با چهره غیرفعال شد."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"دوباره امتحان کنید."</string>
@@ -890,7 +897,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"ماندن در این صفحه"</string>
<string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nمطمئنید میخواهید این صفحه را ترک کنید؟"</string>
<string name="save_password_label" msgid="6860261758665825069">"تأیید"</string>
- <string name="double_tap_toast" msgid="4595046515400268881">"نکته: برای بزرگنمایی و کوچکنمایی، دو بار ضربه بزنید."</string>
+ <string name="double_tap_toast" msgid="4595046515400268881">"نکته: برای نزدیکنمایی و دورنمایی، دو بار ضربه بزنید."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"تکمیل خودکار"</string>
<string name="setup_autofill" msgid="7103495070180590814">"راهاندازی تکمیل خودکار"</string>
<string name="autofill_window_title" msgid="4107745526909284887">"تکمیل خودکار با <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"تصحیح رنگ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"«میانبر دسترسپذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را روشن کرد"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"«میانبر دسترسپذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را خاموش کرد"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"برای راهاندازی ویژگی دسترسپذیری کنونی، دوباره از «میانبر دسترسپذیری» استفاده کنید"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"قابلیتی را انتخاب کنید که هنگام ضربه زدن روی دکمه «دسترسپذیری» استفاده میشود:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"برای تغییر دادن قابلیتها، دکمه «دسترسپذیری» را لمس کنید و نگهدارید."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"درشتنمایی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d54cb72..4f2531c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Virhe otettaessa Wi-Fi-puheluita käyttöön operaattorille <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Wi-Fi-puhelut: %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Wi-Fi-puhelut (<xliff:g id="SPN">%s</xliff:g>)"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-puhelu"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"WLAN-puhelu (<xliff:g id="SPN">%s</xliff:g>)"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Sormenjälkilaitteisto ei ole käytettävissä."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sormenjälkeä ei voida tallentaa. Poista aiemmin lisätty sormenjälki."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Sormenjälkitunnistimen toiminta aikakatkaistiin. Yritä uudelleen."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Kasvotoiminto aikakatkaistiin. Yritä uudelleen."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Kasvoja ei voi tallentaa."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Kasvotoiminto peruutettu"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Käyttäjä peruutti kasvojentunnistuksen."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Liikaa yrityksiä. Kasvojentodennus ei käytössä."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Yritä uudelleen."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Värinkorjaus"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> otettiin käyttöön esteettömyystilan pikanäppäimellä."</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> poistettiin käytöstä esteettömyystilan pikanäppäimellä."</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Valitse esteettömyystilan oikopolku uudelleen käynnistääksesi esteettömyysominaisuuden"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Valitse toiminto, jonka Esteettömyys-painike aktivoi:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Jos haluat muokata ominaisuuksia, kosketa Esteettömyys-painiketta pitkään."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Suurennus"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 0c0cdea..39889ec 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Une erreur s\'est produite lors de l\'enregistrement des appels Wi-Fi avec votre fournisseur de services : <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Appels Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Appels Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Appel WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Appel WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empreinte digitale ne peut pas être enregistrée. Veuillez supprimer une empreinte existante."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Le temps attribué pour lire l\'empreinte est écoulé. Veuillez essayer de nouveau."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Temps de reconn. visage écoulé. Veuillez réessayer."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Impossible de stocker le visage."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance du visage annulée."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Authentification du visage annulée par l\'utilisateur"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Veuillez réessayer plus tard."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Trop de tentatives. Capt. reconn. visage désactivé."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Réessayez."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correction des couleurs"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Le raccourci d\'accessibilité a activé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Utilisez à nouveau le raccourci d\'accessibilité pour démarrer la fonctionnalité d\'accessibilité actuelle"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choisissez une fonctionnalité à utiliser lorsque vous touchez le bouton d\'accessibilité :"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pour changer des fonctionnalités, maintenez le doigt sur le bouton d\'accessibilité."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zoom"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e153082..9759415 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Problème lors de l\'enregistrement des appels Wi‑Fi avec votre opérateur : <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Appels Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Appels Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Appel WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Appel WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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">"Priorité au mobile"</string>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossible d\'enregistrer l\'empreinte numérique. Veuillez supprimer une empreinte."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Délai de détection de l\'empreinte numérique expiré. Veuillez réessayer."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Délai de détection du visage expiré. Réessayez."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Impossible de stocker les informations du visage."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance faciale annulée."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Authentification faciale annulée par l\'utilisateur."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Réessayez plus tard."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Trop d\'essais. Authentification faciale désactivée."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Réessayez."</string>
@@ -1300,14 +1307,14 @@
<string name="perm_costs_money" msgid="4902470324142151116">"Cela peut engendrer des frais"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="usb_charging_notification_title" msgid="1595122345358177163">"Appareil en charge via USB"</string>
- <string name="usb_supplying_notification_title" msgid="4631045789893086181">"Rechargement via USB de l\'appareil connecté"</string>
+ <string name="usb_supplying_notification_title" msgid="4631045789893086181">"Recharge via USB de l\'appareil connecté"</string>
<string name="usb_mtp_notification_title" msgid="4238227258391151029">"Transfert de fichiers via USB activé"</string>
<string name="usb_ptp_notification_title" msgid="5425857879922006878">"PTP via USB activé"</string>
<string name="usb_tether_notification_title" msgid="3716143122035802501">"Partage de connexion via USB activé"</string>
<string name="usb_midi_notification_title" msgid="5356040379749154805">"MIDI via USB activé"</string>
<string name="usb_accessory_notification_title" msgid="1785694450621427730">"Accessoire USB connecté"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Appuyez ici pour plus d\'options."</string>
- <string name="usb_power_notification_message" msgid="4647527153291917218">"Rechargement de l\'appareil connecté. Appuyez ici pour plus d\'options."</string>
+ <string name="usb_power_notification_message" msgid="4647527153291917218">"Recharge de l\'appareil connecté. Appuyez ici pour plus d\'options."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Accessoire audio analogique détecté"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"L\'appareil connecté n\'est pas compatible avec ce téléphone. Appuyez ici pour en savoir plus."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB activé"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correction des couleurs"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Le raccourci d\'accessibilité a activé <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Utilisez à nouveau le raccourci d\'accessibilité pour démarrer la fonctionnalité d\'accessibilité actuelle"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choisissez une fonctionnalité à utiliser lorsque vous appuyez sur le bouton d\'accessibilité :"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pour changer des fonctionnalités, appuyez de manière prolongée sur le bouton d\'accessibilité."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Agrandissement"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index ba66adf..318dd3e 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Produciuse un problema ao rexistrar as chamadas por wifi co teu operador: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Chamadas por wifi de %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Chamadas por wifi de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Chamada por WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Chamada por WLAN de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -298,8 +302,8 @@
<string name="permgroupdesc_phone" msgid="6234224354060641055">"facer e xestionar chamadas telefónicas"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> realice e xestione chamadas telefónicas?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceder aos datos do sensor sobre as túas constantes vitais"</string>
- <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda aos datos do sensor sobre as túas constantes vitais?"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceder aos datos dos sensores sobre as túas constantes vitais"</string>
+ <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda aos datos dos sensores sobre as túas constantes vitais?"</string>
<string name="permgrouplab_aural" msgid="965607064083134896">"Música"</string>
<string name="permgroupdesc_aural" msgid="4870189506255958055">"acceder á música"</string>
<string name="permgrouprequest_aural" msgid="6787926123071735620">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda á túa música?"</string>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impresión dixital non dispoñible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Non se pode almacenar a impresión dixital. Elimina unha impresión dixital existente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Esgotouse o tempo de espera da impresión dixital. Téntao de novo."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Esgotouse o tempo de espera. Téntao de novo."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Non se puido almacenar a cara."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Cancelouse a operación relacionada coa cara"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"O usuario cancelou a autenticación da cara."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Téntao de novo máis tarde."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autenticación desactivada."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Téntao de novo."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atallo de accesibilidade activou <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atallo de accesibilidade desactivou <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Para iniciar a función de accesibilidade actual, utiliza de novo o atallo de accesibilidade"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolle que función queres utilizar cando toques o botón Accesibilidade:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para cambiar as funcións, mantén premido o botón Accesibilidade."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliación"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 2b96f3f..13090c6 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"તમારા કૅરિઅરમાં વાઇ-ફાઇ કૉલિંગ રજિસ્ટર કરવામાં સમસ્યા આવી: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s વાઇ-ફાઇ કૉલિંગ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> વાઇ-ફાઇ કૉલિંગ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN કૉલ"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN કૉલ"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ફિંગરપ્રિન્ટ સંગ્રહિત કરી શકાતી નથી. કૃપા કરીને અસ્તિત્વમાંની ફિંગરપ્રિન્ટ દૂર કરો."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ફિંગરપ્રિન્ટનો સમય બાહ્ય થયો. ફરી પ્રયાસ કરો."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ચહેરા માટેનો સમય સમાપ્ત થયો. ફરી પ્રયાસ કરો."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ચહેરો સંગ્રહિત કરી શકાશે નહીં."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ચહેરા સંબંધિત કાર્યવાહી રદ કરવામાં આવી છે."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"વપરાશકર્તાએ ચહેરા પ્રમાણીકરણ રદ કર્યુ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ઘણા બધા પ્રયત્નો. થોડા સમય પછી ફરી પ્રયાસ કરો."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ઘણા બધા પ્રયત્નો. ચહેરાનું પ્રમાણીકરણ બંધ કરવામાં આવ્યું છે."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ફરી પ્રયાસ કરો."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"રંગ સુધારણા"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ઍક્સેસિબિલિટી શૉર્ટકટે <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ઍક્સેસિબિલિટી શૉર્ટકટે <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"વર્તમાન ઍક્સેસિબિલિટી સુવિધાને શરૂ કરવા માટે ફરીથી ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરો"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"જ્યારે તમે ઍક્સેસિબિલિટી બટન પર ટૅપ કરો, ત્યારે ઉપયોગ કરવાની સુવિધા પસંદ કરો:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"સુવિધાઓ બદલવા માટે, ઍક્સેસિબિલિટી બટન દબાવી રાખો."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"વિસ્તૃતીકરણ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6afe603..bebd489 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"वाई-फ़ाई कॉलिंग की सुविधा के लिए आपकी मोबाइल और इंटरनेट सेवा देने वाली कंपनी के साथ रजिस्टर करने से जुड़ी समस्या: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s वाई-फ़ाई कॉलिंग"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> वाई-फ़ाई कॉलिंग"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN कॉल"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN कॉल"</string>
+ <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>
<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>
@@ -526,6 +530,8 @@
<!-- no translation found for biometric_not_recognized (5770511773560736082) -->
<skip />
<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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
@@ -562,6 +568,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"चेहरे की पहचान का समय खत्म हुआ. फिर से कोशिश करें."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"चेहरा सेव करने की सीमा पूरी हो गई है."</string>
<string name="face_error_canceled" msgid="283945501061931023">"चेहरा पहचानने की कार्रवाई रद्द की गई."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"उपयोगकर्ता ने चेहरे की पहचान रद्द कर दी."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"कई बार कोशिश की गई. बाद में कोशिश करें."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"कई बार कोशिश की. चेहरा पहचानने की सुविधा बंद हुई."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"फिर से कोशिश करें."</string>
@@ -1618,6 +1625,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"रंग में सुधार करने की सुविधा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"सुलभता शॉर्टकट ने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू किया"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"सुलभता शॉर्टकट ने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद किया"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"मौजूदा सुलभता सुविधा शुरू करने के लिए \'सुलभता शॉर्टकट\' का फिर से इस्तेमाल करें"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"सुलभता बटन पर टैप करते समय इस्तेमाल की जाने वाली सुविधा चुनें:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"सुविधाओं में बदलाव करने के लिए, सुलभता बटन को दबाकर रखें."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"बड़ा करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e7b6b5f..839d423 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -129,10 +129,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Poteškoća s registracijom Wi‑Fi poziva kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi pozivanje"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi pozivi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Poziv putem WLAN-a"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> poziv putem WLAN-a"</string>
+ <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>
<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>
@@ -527,6 +531,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta nije pohranjen. Uklonite postojeći otisak prsta."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Isteklo je vrijeme čekanja za otisak prsta. Pokušajte ponovo."</string>
@@ -563,6 +569,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Isteklo je vrijeme čekanja za lice. Pokušajte opet"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće pohraniti lice."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Otkazana je radnja s licem."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autentifikaciju lica otkazao je korisnik."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Previše pokušaja. Autentifikacija lica onemogućena"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Pokušajte ponovo."</string>
@@ -1640,6 +1647,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcija boje"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečac pristupačnosti uključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečac pristupačnosti isključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Ponovo upotrijebite prečac pristupačnosti da biste pokrenuli trenutačnu značajku pristupačnosti"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite značajku koju ćete upotrebljavati kada dodirnete gumb Pristupačnost:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Da biste promijenili značajke, dodirnite i zadržite gumb Pristupačnost."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Povećavanje"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3d9a35b..bc54bfa 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Hiba történt a Wi‑Fi-hívás szolgáltatónál való regisztrálása során: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi-hívás"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi-hívás"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-hívás"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN-hívás"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Az ujjlenyomathoz szükséges hardver nem érhető el."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Az ujjlenyomat nem tárolható. Távolítson el egy meglévő ujjlenyomatot."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Az ujjlenyomat-beolvasási műveletkor időtúllépés történt. Próbálkozzon újra."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Időtúllépés az arcbeolvasásnál. Próbálja újra."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Az arc nem tárolható."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Az arccal kapcsolatos művelet törölve."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Az arc hitelesítését a felhasználó visszavonta."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Túl sok próbálkozás. Próbálja újra később."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Túl sok próbálkozás. Arcfelismerés letiltva."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Próbálkozzon újra."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Színkorrekció"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"A Kisegítő lehetőségek gyorsparancsa bekapcsolta a következő szolgáltatást: <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"A Kisegítő lehetőségek gyorsparancsa kikapcsolta a következő szolgáltatást: <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Használja újból a Kisegítő lehetőségek gyorsparancsát az aktuális kisegítő lehetőségek elindításához"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Válassza ki a Kisegítő lehetőségek gombra koppintáskor használni kívánt funkciót:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"A funkciók módosításához tartsa lenyomva a Kisegítő lehetőségek gombot."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Nagyítás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 6a5f287..4a828fa 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Wi‑Fi-ի միջոցով արվող զանգերը չհաջողվեց գրանցել ձեր օպերատորի մոտ՝ <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi զանգեր"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi-ի միջոցով զանգեր"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Զանգ WLAN ցանցով"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN ցանցով զանգեր"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Մատնահետքի սարքն անհասանելի է:"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Հնարավոր չէ պահել մատնահետքը: Հեռացրեք առկա մատնահետքը:"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Մատնահետքի գրանցման ժամանակը սպառվել է: Փորձեք նորից:"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Ժամանակը սպառվել է: Նորից փորձեք:"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Դեմքը հնարավոր չէ պահել։"</string>
<string name="face_error_canceled" msgid="283945501061931023">"Դեմքի ճանաչումը չեղարկվել է։"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Դեմքի ճանաչումը չեղարկվել է օգտատիրոջ կողմից:"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Չափից շատ փորձեր եք կատարել: Փորձեք ավելի ուշ:"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Չափից շատ փորձեր եք կատարել: Դեմքի ճանաչման գործառույթն անջատվել է։"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Նորից փորձեք:"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Գունաշտկում"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Մատչելիության դյուրանցումն միացրել է <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Մատչելիության դյուրանցումն անջատել է <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Հատուկ գործառույթների դյուրանցումը գործարկելու համար նորից օգտագործեք համապատասխան դյուրանցումը"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Ընտրեք որևէ գործառույթ, որը կօգտագործվի Հատուկ գործառույթներ կոճակին հպելու դեպքում՝"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Գործառույթները փոխելու համար հպեք և պահեք Հատուկ գործառույթներ կոճակը։"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Խոշորացում"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 6d1da48..167082d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Terjadi masalah saat mendaftarkan panggilan Wi‑Fi dengan operator Anda: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Panggilan Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Panggilan Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Panggilan WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Panggilan WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware sidik jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sidik jari tidak dapat disimpan. Hapus sidik jari yang ada."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Waktu sidik jari habis. Coba lagi."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Waktu tunggu wajah habis. Harap coba lagi."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Wajah tidak dapat disimpan."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Pemrosesan wajah dibatalkan."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autentikasi wajah dibatalkan oleh pengguna."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percobaan. Coba lagi nanti."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Terlalu sering dicoba. Autentikasi wajah dinonaktifkan."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Coba lagi."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Koreksi Warna"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan Aksesibilitas mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Aksesibilitas menonaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gunakan Pintasan Aksesibilitas lagi untuk memulai fitur aksesibilitas saat ini"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih fitur yang akan digunakan saat menge-tap tombol Aksesibilitas:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Untuk mengubah fitur, sentuh & tahan tombol Aksesibilitas."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Pembesaran"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 4d7996f..0c7e6b8 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Vandamál kom upp við að skrá Wi‑Fi símtöl hjá símafyrirtækinu: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi símtöl"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi símtöl"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-símtal"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN-símtal"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingrafarsvélbúnaður ekki til staðar."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ekki er hægt að vista fingrafarið. Fjarlægðu eitthvert af fingraförunum sem fyrir eru."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tímamörk runnu út fyrir fingrafar. Reyndu aftur."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Tímamörk runnu út fyrir andlit. Reyndu aftur."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Ekki tókst að geyma andlit."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Hætt við andlitsgreiningu."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Notandi hætti við andlitsgreiningu."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Of margar tilraunir. Reyndu aftur síðar."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Of margar tilraunir. Slökkt á andlitsgreiningu."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Reyndu aftur."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Litaleiðrétting"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Flýtileið aðgengisstillingar kveikti á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Flýtileið aðgengisstillingar slökkti á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Notaðu flýtileið aðgengisstillingar aftur til að opna aðgengiseiginleika"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Veldu eiginleika sem á að nota þegar ýtt er á aðgengishnappinn:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Haltu fingri á aðgengishnappinum til að breyta eiginleikum."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Stækkun"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 44818a3..a6d5d23 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Si è verificato un problema con la registrazione delle chiamate Wi‑Fi con l\'operatore: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Chiamata Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Chiamate Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Chiamata WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Chiamata WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware per l\'impronta digitale non disponibile."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossibile memorizzare l\'impronta digitale. Rimuovi un\'impronta esistente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timeout impronta digitale. Riprova."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Timeout operazione associata al volto. Riprova."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Il volto non può essere memorizzato."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operazione associata al volto annullata."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autenticazione del volto annullata dall\'utente."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Troppi tentativi. Riprova più tardi."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Troppi tentativi. Autenticazione disattivata."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Riprova."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correzione del colore"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"La scorciatoia Accessibilità ha attivato <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"La scorciatoia Accessibilità ha disattivato <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Usa di nuovo la scorciatoia Accessibilità per avviare l\'attuale funzione di accessibilità"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Scegli una funzione da usare quando tocchi il pulsante Accessibilità:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Per cambiare le funzioni, tocca e tieni premuto il pulsante Accessibilità."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ingrandimento"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index fd5c2a1..48933f9 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"אירעה בעיה ברישום שיחות Wi-Fi אצל הספק שלך: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"שיחות Wi-Fi של %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"שיחות Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"שיחה ברשת אלחוטית"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"שיחת WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"החומרה בשביל טביעת אצבע אינה זמינה."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"לא ניתן לאחסן טביעת אצבע. הסר טביעת אצבע קיימת."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"חלף הזמן הקצוב לטביעת אצבע. נסה שוב."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"חלף הזמן הקצוב לזיהוי הפנים. יש לנסות שוב."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"לא ניתן לשמור את הפנים."</string>
<string name="face_error_canceled" msgid="283945501061931023">"פעולת הפנים בוטלה."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"זיהוי הפנים בוטל על ידי המשתמש."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"יותר מדי ניסיונות. אימות הפנים הושבת."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"יש לנסות שוב."</string>
@@ -1228,13 +1235,13 @@
<string name="volume_call" msgid="3941680041282788711">"עוצמת קול בשיחה"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"עוצמת הקול בשיחה ב-Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"עוצמת קול של התראה"</string>
- <string name="volume_notification" msgid="2422265656744276715">"עוצמת קול של הודעות"</string>
+ <string name="volume_notification" msgid="2422265656744276715">"עוצמת הקול של ההתראות"</string>
<string name="volume_unknown" msgid="1400219669770445902">"עוצמת קול"</string>
<string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"עוצמת קול של Bluetooth"</string>
<string name="volume_icon_description_ringer" msgid="3326003847006162496">"עוצמת קול של רינגטון"</string>
<string name="volume_icon_description_incall" msgid="8890073218154543397">"עוצמת קול של שיחות"</string>
<string name="volume_icon_description_media" msgid="4217311719665194215">"עוצמת קול של מדיה"</string>
- <string name="volume_icon_description_notification" msgid="7044986546477282274">"עוצמת קול של הודעות"</string>
+ <string name="volume_icon_description_notification" msgid="7044986546477282274">"עוצמת הקול של ההתראות"</string>
<string name="ringtone_default" msgid="3789758980357696936">"רינגטון ברירת מחדל"</string>
<string name="ringtone_default_with_actual" msgid="1767304850491060581">"ברירת מחדל (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="7937634392408977062">"ללא"</string>
@@ -1502,7 +1509,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"מחק את הפריטים"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"בטל את פעולות המחיקה"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"אל תעשה דבר כרגע"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"בחר חשבון"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"בחירת חשבון"</string>
<string name="add_account_label" msgid="2935267344849993553">"הוספת חשבון"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"הוספת חשבון"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"הוסף"</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"תיקון צבעים"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל על-ידי קיצור הדרך לנגישות"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת על-ידי קיצור הדרך לנגישות"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"יש להשתמש שוב במקש הקיצור לנגישות כדי להפעיל את תכונת הנגישות הנוכחית"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"בחר תכונה שתופעל כשתלחץ על הלחצן \'נגישות\':"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"כדי להחליף תכונה, גע בלחצן \'נגישות\' והחזק אותו."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"הגדלה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4915111..994039c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"携帯通信会社への Wi‑Fi 通話の登録中に問題が発生しました(<xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Wi-Fi通話(%s)"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN 通話"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN 通話"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"指紋ハードウェアは使用できません。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋を保存できません。既存の指紋を削除してください。"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"読み取りのタイムアウトです。もう一度お試しください。"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"顔の情報を保存できません。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"顔の操作をキャンセルしました。"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"顔の認証がユーザーによりキャンセルされました。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"試行回数の上限です。後でもう一度お試しください。"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"試行回数の上限です。顔認証は無効になりました。"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"もう一度お試しください。"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"色補正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ユーザー補助機能のショートカットにより <xliff:g id="SERVICE_NAME">%1$s</xliff:g> は ON になっています"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ユーザー補助機能のショートカットにより <xliff:g id="SERVICE_NAME">%1$s</xliff:g> は OFF になっています"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"現在のユーザー補助機能を開始するには、ユーザー補助機能のショートカットをもう一度使用してください"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"[ユーザー補助] ボタンをタップした場合に使用する機能を選択してください。"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"機能を変更するには、[ユーザー補助] ボタンを押し続けてください。"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"拡大"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 51a4c28..a9ce748 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Wi‑Fi დარეკვის თქვენს ოპერატორთან რეგისტრირებისას შეცდომა წარმოიქმნა: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s დარეკვა Wi-Fi-ს მეშვეობით"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi დარეკვა"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN ზარი"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN ზარი"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"თითის ანაბეჭდის აპარატურა არ არის ხელმისაწვდომი."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"თითის ანაბეჭდის შენახვა ვერ ხერხდება. გთხოვთ, ამოშალოთ არსებული თითის ანაბეჭდი."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"თითის ანაბეჭდის ლოდინის დრო ამოიწურა. სცადეთ ხელახლა."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"სახის ამოცნობის დრო ამოიწურა. ცადეთ ხელახლა."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"სახის შენახვა ვერ მოხერხდა."</string>
<string name="face_error_canceled" msgid="283945501061931023">"სახის ამოცნობა გაუქმდა."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"სახის ავტორიზაცია გაუქმდა მომხმარებლის მიერ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"დაფიქსირდა ბევრი მცდელობა. ცადეთ მოგვიანებით."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"დაფიქსირდა ბევრი მცდელობა. სახის ამოცნობა გაითიშა."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ცადეთ ხელახლა."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ფერთა კორექცია"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"მარტივი წვდომის მალსახმობმა ჩართო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"მარტივი წვდომის მალსახმობმა გამორთო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"მარტივი წვდომის ამჟამინდელი ფუნქციის გასაშვებად გამოიყენეთ მარტივი წვდომის მალსახმობი"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"აირჩიეთ მარტივი წვდომის ღილაკზე შეხებისას გამოსაყენებელი ფუნქცია:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ფუნქციების შესაცვლელად ხანგრძლივად შეეხეთ მარტივი წვდომის ღილაკს."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"გადიდება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 18be1e6..a8b527b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Оператордың Wi‑Fi қоңырауын тіркеу кезінде қате шықты: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi арқылы қоңырау шалу"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi қоңыраулары"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN қоңырауы"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN қоңырауы"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Саусақ ізі жабдығы қолжетімді емес."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Саусақ ізін сақтау мүмкін емес. Бар саусақ ізін жойыңыз."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Саусақ ізін күту уақыты бітті. Әрекетті қайталаңыз."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Күту уақыты бітті. Әрекетті қайталаңыз."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Бетті сақтау мүмкін емес."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Бетті танудан бас тартылды."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Пайдаланушы бетті тану әрекетінен бас тартты."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Тым көп әрекет жасалды. Кейінірек қайталаңыз."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Тым көп әрекет жасалды. Бетті тану функциясы өшірілді."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Қайталап көріңіз."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Түсті түзету"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Арнайы мүмкіндіктер таңбашасы <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін қосты"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Арнайы мүмкіндіктер таңбашасы <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін өшірді"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Арнайы мүмкіндіктер функциясын іске қосу үшін оның таңбашасын пайдаланыңыз"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"\"Арнайы мүмкіндіктер\" түймесін түрткенде пайдаланатын мүмкіндікті таңдаңыз:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Мүмкіндіктерді өзгерту үшін \"Арнайы мүмкіндіктер\" түймесін түртіп, ұстап тұрыңыз."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ұлғайту"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 091fa71..2620ce4 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"មានបញ្ហាក្នុងការចុះឈ្មោះការហៅតាម Wi‑Fi ជាមួយក្រុមហ៊ុនសេវាទូរសព្ទរបស់អ្នក៖ <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"ការហៅតាមរយៈ Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ការហៅតាម Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"ការហៅតាម WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> ការហៅតាម WLAN"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ផ្នែករឹងស្នាមម្រាមដៃមិនមានទេ។"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"មិនអាចផ្ទុកស្នាមម្រាមដៃទេ។ សូមយកស្នាមម្រាមដៃដែលមានស្រាប់ចេញ។"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ការផ្តិតម្រាមដៃបានអស់ពេល។ សូមព្យាយាមម្តងទៀត។"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ការសម្គាល់ផ្ទៃមុខបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"មិនអាចរក្សាទុកផ្ទៃមុខបានទេ។"</string>
<string name="face_error_canceled" msgid="283945501061931023">"បានបោះបង់ប្រតិបត្តិការចាប់ផ្ទៃមុខ។"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ការផ្ទៀងផ្ទាត់មុខត្រូវបានបោះបង់ដោយអ្នកប្រើប្រាស់។"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ព្យាយាមចូលច្រើនពេកហើយ។ បានបិទការផ្ទៀងផ្ទាត់ផ្ទៃមុខ។"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"សូមព្យាយាមម្ដងទៀត។"</string>
@@ -1618,6 +1625,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ការកែពណ៌"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ផ្លូវកាត់ភាពងាយស្រួលបានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ផ្លូវកាត់ភាពងាយស្រួលបានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ប្រើផ្លូវកាត់ភាពងាយស្រួលម្ដងទៀត ដើម្បីចាប់ផ្ដើមមុខងារភាពងាយប្រើបច្ចុប្បន្ន"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ជ្រើសរើសមុខងារដែលត្រូវប្រើ នៅពេលដែលអ្នកចុចប៊ូតុងភាពងាយស្រួល៖"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ដើម្បីផ្លាស់ប្តូរមុខងារ សូមចុចប៊ូតុងភាពងាយស្រួលឲ្យជាប់។"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ការពង្រីក"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index e228d35..1b83ec2 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"ನಿಮ್ಮ ವಾಹಕದೊಂದಿಗೆ ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ ಸಮಸ್ಯೆ ನೋಂದಾಯಿಸುವಿಕೆ: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN ಕರೆ"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN ಕರೆ"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಬೆರಳಚ್ಚು ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಬೆರಳಚ್ಚು ತೆಗೆದುಹಾಕಿ."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ಬೆರಳಚ್ಚು ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ಮುಖ ಸಮಯದ ಅವಧಿಯನ್ನು ತಲುಪಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ಮುಖವನ್ನು ಸಂಗ್ರಹಿಸಲಾಗುವುದಿಲ್ಲ."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ಮುಖದ ಕಾರ್ಯಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ಮುಖ ದೃಢೀಕರಣವನ್ನು ಬಳಕೆದಾರರ ಮೂಲಕ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ಹಲವು ಪ್ರಯತ್ನ. ಮುಖದ ದೃಢೀಕರಣ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಿದೆ"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಫ್ ಮಾಡಿದೆ"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ಪ್ರಸ್ತುತ ಪ್ರವೇಶದ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಪ್ರಾರಂಭಿಸಲು ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃ ಬಳಸಿ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಟ್ಯಾಪ್ ಮಾಡಿದಾಗ ಬಳಸುವುದಕ್ಕಾಗಿ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಆರಿಸಿ:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಬದಲಾಯಿಸಲು, ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಒತ್ತಿಹಿಡಿದುಕೊಳ್ಳಿ."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ಹಿಗ್ಗಿಸುವಿಕೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index d37a475..6223dab 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"이동통신사를 통해 Wi‑Fi 통화를 등록하는 중에 문제가 발생했습니다. 오류 코드: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi 통화"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi 통화"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN 통화"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN 통화"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"지문 인식 하드웨어를 사용할 수 없습니다."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"지문을 저장할 수 없습니다. 기존 지문을 삭제하세요."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"지문 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"얼굴 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"얼굴을 저장할 수 없습니다."</string>
<string name="face_error_canceled" msgid="283945501061931023">"얼굴 인식 작업이 취소되었습니다."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"사용자가 얼굴 인증을 취소했습니다."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"시도 횟수가 너무 많아 얼굴 인증이 사용 중지되었습니다."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"다시 시도해 보세요."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"색상 보정"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"접근성 단축키로 인해 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"접근성 단축키로 인해 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"현재 접근성 기능을 시작하려면 접근성 단축키를 다시 사용하세요"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"접근성 버튼을 탭할 때 사용할 기능을 선택하세요."</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"기능을 변경하려면 접근성 버튼을 길게 터치하세요."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"확대"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index cf5f531..f49746f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Байланыш операторуңуз менен Wi-Fi аркылуу чалууну каттоодо ката кетти: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Чалуу"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi аркылуу чалуу"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN аркылуу чалуу"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN аркылуу чалуу"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Манжа изин сактоо мүмкүн эмес. Учурдагы манжа изин алып салыңыз."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Жүздүн аныктыгын текшерүүнү күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Жүздү сактоо мүмкүн эмес."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Жүздүн аныктыгын текшерүү жокко чыгарылды."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Жүздүн аныктыгын текшерүү колдонуучу аркылуу жокко чыгарылды."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Өтө көп жолу аракет жасадыңыз. Кийинчерээк кайра аракет кылыңыз."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Өтө көп жолу аракет жасадыңыз. Жүздүн аныктыгын текшерүү сенсору өчүрүлдү."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Кайра аракет кылыңыз."</string>
@@ -1618,6 +1625,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Түсүн тууралоо"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Атайын мүмкүнчүлүктөр кыска жолу <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын күйгүздү"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Атайын мүмкүнчүлүктөр кыска жолу <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын өчүрдү"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Учурдагы атайын мүмкүнчүлүктөр функциясын иштетүү үчүн кыска жолду кайра колдонуңуз"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштетиле турган функцияны тандаңыз:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Функцияларды өзгөртүү үчүн Атайын мүмкүнчүлүктөр баскычын басып, кармап туруңуз."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Чоңойтуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7d8a4e2..9fc6033 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"ເກີດບັນຫາໃນການລົງທະບຽນການໂທ Wi‑Fi ກັບຜູ້ໃຫ້ບໍລິການຂອງທ່ານ: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"ການໂທ %s Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi Calling"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN Call"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Call"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ບໍ່ມີຮາດແວລາຍນີ້ວມືໃຫ້ຢູ່."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ບໍ່ສາມາດເກັບຮັກສາລາຍນີ້ວມືໄວ້ໄດ້. ກະລຸນາເອົາລາຍນີ້ວມືທີ່ມີຢູ່ອອກໄປ."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ເວລາລາຍນີ້ວມືບໍ່ເຂົ້າເຖິງໄດ້. ລອງໃໝ່ອີກ."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ໝົດເວລາກວດໃບໜ້າແລ້ວ. ກະລຸນາລອງອີກຄັ້ງ."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ບໍ່ສາມາດເກັບຮັກສາໃບໜ້າໄວ້ໄດ້."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ຍົກເລີກການດຳເນີນການກັບໃບໜ້າແລ້ວ."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ຜູ້ໃຊ້ຍົກເລີກການພິສູດຢືນຢັນໃບໜ້າແລ້ວ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ປິດນຳໃຊ້ການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າແລ້ວ."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ລອງອີກຄັ້ງ."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ການແກ້ໄຂຄ່າສີ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງອີກເທື່ອໜຶ່ງເພື່ອຊອກຫາຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງໃນປັດຈຸບັນ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ເລືອກຄຸນສົມບັດທີ່ຈະໃຊ້ເມື່ອທ່ານແຕະປຸ່ມການຊ່ວຍເຂົ້າເຖິງ:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ເພື່ອປ່ຽນຄຸນສົມບັດ, ໃຫ້ແຕະປຸ່ມການຊ່ວຍເຂົ້າເຖິງຄ້າງໄວ້."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ການຂະຫຍາຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a0015c3..d6869c1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Kilo problema registruojant „Wi‑Fi“ skambinimą pas operatorių: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"„%s“ „Wi-Fi“ skambinimas"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> „Wi-Fi“ skambinimas"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN skambutis"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN skambutis"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</string-array>
<string name="biometric_not_recognized" msgid="5770511773560736082">"Neatpažinta"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Kontrolinis kodas 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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Kontrolinio kodo aparatinė įranga nepasiekiama."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Negalima išsaugoti kontrolinio kodo. Pašalinkite esamą kontrolinį kodą."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Baigėsi kontrolinio kodo nustatymo skirtasis laikas. Bandykite dar kartą."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Baigėsi veido atpaž. skirt. laik. Band. dar kartą."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Nepavyko išsaugoti veido duomenų."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Veido atpažinimo operacija atšaukta."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Veido autentifikavimą atšaukė naudotojas."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Per daug bandymų. Veido autentifik. išjungtas."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Bandykite dar kartą."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Spalvų taisymas"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pritaikymo neįgaliesiems sparčiuoju klavišu buvo įjungta „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pritaikymo neįgaliesiems sparčiuoju klavišu buvo išjungta „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Norėdami įjungti dabartinę pritaikymo neįgaliesiems funkciją, dar kartą naudokite spartųjį pritaikymo neįgaliesiems klavišą"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pasirinkite funkciją, kuri bus naudojama, kai paliesite pritaikymo neįgaliesiems mygtuką:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Jei norite pakeisti funkcijas, palieskite ir palaikykite pritaikymo neįgaliesiems mygtuką."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Didinimas"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a1aebe1..156a1a9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -129,10 +129,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Reģistrējot Wi-Fi zvanus pie mobilo sakaru operatora, radās problēma: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi zvani"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi zvani"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN zvans"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN zvans"</string>
+ <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>
<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>
@@ -527,6 +531,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Nospieduma aparatūra nav pieejama."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Pirkstu nospiedumu nevar saglabāt. Lūdzu, noņemiet esošu pirksta nospiedumu."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Pirkstu nospiedumu nolasīšanas aparatūras noildze. Mēģiniet vēlreiz."</string>
@@ -563,6 +569,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Sejas datu nolasīšanas noildze. Mēģiniet vēlreiz."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Sejas datus nevar saglabāt."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Darbība ar sejas datiem atcelta."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Lietotājs atcēla sejas autentificēšanu."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Par daudz mēģinājumu. Sejas atpazīšana atspējota."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Mēģiniet vēlreiz."</string>
@@ -1640,6 +1647,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Krāsu korekcija"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pieejamības saīsne aktivizēja lietotni <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pieejamības saīsne deaktivizēja lietotni <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Lai sāktu izmantot pašreizējo pieejamības funkciju, vēlreiz izmantojiet pieejamības saīsni."</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izvēlieties funkciju, ko izmantot, kad pieskaraties pogai Pieejamība."</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Lai mainītu funkcijas, pieskarieties pogai Pieejamība un turiet to."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Palielinājums"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0ab31b3..8bca75e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Проблем при регистрирањето на функцијата „Повици преку Wi‑Fi“ со операторот: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Повикување преку Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Повици преку Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Повик преку WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Повик преку WLAN на <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардвер за отпечаток од прст не е достапен."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатокот не може да се складира. Отстранете го постоечкиот отпечаток."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Истече времето за проверка на лице. Повторен обид."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Лицето не може да се чува."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Операцијата со лице се откажа."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Проверката на лицето е откажана од корисникот."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Премногу обиди. Обидете се повторно подоцна."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Премногу обиди. Проверката на лице е оневозможена."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Обидете се повторно."</string>
@@ -1619,6 +1626,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Корекција на бои"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Кратенката за пристапност ја вклучи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Кратенката за пристапност ја исклучи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Повторно употребете ја кратенката за пристапност за да ја стартувате тековната функција за пристапност"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изберете функција за користење кога ќе го допрете копчето за „Пристапност“."</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"За променување функции, допрете го и задржете го копчето за „Пристапност“."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Зголемување"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index eafd6a4..1efdc72 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"നിങ്ങളുടെ കാരിയർ ഉപയോഗിച്ച് വൈഫൈ കോളിംഗ് രജിസ്റ്റർ ചെയ്യുന്നതിൽ പ്രശ്നം: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s വൈഫൈ കോളിംഗ്"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> വൈഫൈ കോളിംഗ്"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN കോൾ"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN കോൾ"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ ലഭ്യമല്ല."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"വിരലടയാളം സംഭരിക്കാനാവില്ല. നിലവിലുള്ള വിരലടയാളം നീക്കംചെയ്യുക."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"മുഖം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"മുഖം സൂക്ഷിക്കാനാവില്ല."</string>
<string name="face_error_canceled" msgid="283945501061931023">"മുഖത്തിന്റെ പ്രവർത്തനം റദ്ദാക്കി."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"മുഖം പരിശോധിച്ചുറപ്പിക്കൽ ഉപയോക്താവ് റദ്ദാക്കി."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"നിരവധി തവണ ശ്രമിച്ചു. മുഖം തിരിച്ചറിയൽ പ്രവർത്തനരഹിതമാക്കി."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"വീണ്ടും ശ്രമിക്കുക."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"വർണ്ണം ക്രമീകരിക്കൽ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ഉപയോഗസഹായിക്കുള്ള കുറുക്കുവഴി <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓൺ ചെയ്തിരിക്കുന്നു"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ഉപയോഗസഹായിക്കുള്ള കുറുക്കുവഴി <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫ് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"നിലവിലെ ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കാൻ വീണ്ടും ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"നിങ്ങൾ ഉപയോഗസഹായി ബട്ടൺ ടാപ്പുചെയ്യുമ്പോൾ ഉപയോഗിക്കുന്നതിന് ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ഫീച്ചറുകൾ മാറ്റുന്നതിന് ഉപയോഗസഹായി ബട്ടൺ സ്പർശിച്ചുപിടിക്കുക."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"മാഗ്നിഫിക്കേഷൻ"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 12e3186..86ae178 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Таны оператор компанийн Wi‑Fi дуудлагыг бүртгэхэд асуудал гарлаа: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi Дуудлага"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi дуудлага"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN дуудлага"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN дуудлага"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хурууны хээний тоног төхөөрөмж бэлэн бус байна."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Хурууны хээг хадгалах боломжгүй байна. Одоо байгаа хурууны хээг арилгана уу."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Царай таниулах хугацаа дууслаа. Дахин оролдоно уу."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Царайг хадгалах боломжгүй байна."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Царайны үйл ажиллагааг цуцаллаа."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Хэрэглэгч царайгаар баталгаажуулахыг цуцалсан байна."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Хэт олон удаа оролдлоо. Дараа дахин оролдоно уу."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Хэт олон удаа оролдлоо. Царай танилтыг идэвхгүй болголоо."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Дахин оролдоно уу."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Өнгөний засвар"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Хүртээмжийн товчлол <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаасан"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Хүртээмжийн товчлол <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраасан"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Одоогийн хүртээмжит онцлогийг эхлүүлэхийн тулд нэвтрэлтийн товчлолыг ашиглах"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Хүртээмжийн товчлуурыг товших үедээ ашиглах онцлогийг сонгоно уу:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Онцлогийг өөрчлөхийн тулд Хүртээмжийн товчлуурыг дараад хүлээнэ үү."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Томруулах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index fe248c1..70ed012 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"तुमच्या या वाहकासह वाय-फाय कॉलिंग नोंदणी करताना समस्या आली आहे:<xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s वाय-फाय कॉलिंग"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> वाय-फाय कॉलिंग"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN कॉल"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN कॉल"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फिंगरप्रिंट हार्डवेअर उपलब्ध नाही."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फिंगरप्रिंट स्टोअर केले जाऊ शकत नाही. कृपया विद्यमान फिंगरप्रिंट काढा."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"फिंगरप्रिंट टाइमआउट झाले. पुन्हा प्रयत्न करा."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"चेहरा टाइमआउट झाला. पुन्हा प्रयत्न करा."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"चेहरा स्टोअर केला जाऊ शकत नाही."</string>
<string name="face_error_canceled" msgid="283945501061931023">"चेहरा ऑपरेशन रद्द केले गेले."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"वापरकर्त्याने चेहरा ऑथेंटिकेशन रद्द केले."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"खूप जास्त प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"खूप जास्त प्रयत्न केले. चेहरा ऑथेंटिकेशन बंद केले गेले."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"पुन्हा प्रयत्न करा."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"रंग सुधारणा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> चालू केली"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केली"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"सध्याचे अॅक्सेसिबिलिटी वैशिष्ट्य पुन्हा सुरू करण्यासाठी अॅक्सेसिबिलिटी शॉर्टकट वापरा"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"तुम्ही प्रवेशयोग्यता बटण दाबल्यावर वापरण्यासाठी वैशिष्ट्य निवडा:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"वैशिष्ट्ये बदलण्यासाठी, प्रवेशयोग्यता बटणाला स्पर्श करा आणि धरून ठेवा."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"मोठे करणे"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index a813a7b..208fed6 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Terdapat masalah semasa mendaftarkan panggilan Wi-Fi dengan pembawa anda: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Panggilan Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Panggilan Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Panggilan WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Panggilan WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Perkakasan cap jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Cap jari tidak dapat disimpan. Sila alih keluar cap jari sedia ada."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tamat masa cap jari dicapai. Cuba lagi."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Tamat masa wajah dicapai. Cuba lagi."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Wajah tidak dapat disimpan."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Pengendalian wajah dibatalkan."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Pengesahan wajah dibatalkan oleh pengguna."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Terlalu banyak percubaan. Pengesahan wajah dilumpuhkan."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Cuba lagi."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Pembetulan Warna"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan kebolehaksesan menghidupkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Kebolehaksesan mematikan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gunakan Pintasan Kebolehaksesan sekali lagi untuk memulakan ciri kebolehaksesan semasa"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih ciri yang hendak digunakan apabila anda mengetik butang Kebolehaksesan:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Untuk menukar ciri, sentuh & tahan butang Kebolehaksesan."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Pembesaran"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 812ff75..71dd4b6 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"သင်၏ ဝန်ဆောင်မှုပေးသူဖြင့် Wi‑Fi ခေါ်ဆိုမှုကို မှတ်ပုံတင်ရာတွင် ပြဿနာရှိနေသည်− <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi ခေါ်ဆိုမှု"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi ခေါ်ဆိုမှု"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN ခေါ်ဆိုမှု"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN ခေါ်ဆိုမှု"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"လက်ဗွေရာ ဟာ့ဒ်ဝဲ မရနိုင်ပါ။"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"လက်ဗွေရာ သိုလှောင်၍မရပါ။ ကျေးဇူးပြု၍ ရှိပြီးလက်ဗွေရာအား ဖယ်ရှားပါ။"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"လက်ဗွေရာအချိန်ကုန် သွားပါသည်။ ထပ်မံကြိုးစားပါ။"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"မျက်နှာ သက်တမ်းကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"မျက်နှာကို သိမ်း၍မရပါ။"</string>
<string name="face_error_canceled" msgid="283945501061931023">"မျက်နှာ ဆောင်ရွက်ခြင်းကို ပယ်ဖျက်လိုက်ပါပြီ။"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"အသုံးပြုသူက မျက်နှာအထောက်အထားစိစစ်မှု မလုပ်တော့ပါ။"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ နောက်မှထပ်စမ်းပါ။"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ ပိတ်လိုက်ပါပြီ။"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ထပ်စမ်းကြည့်ပါ။"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"အရောင်ပြင်ဆင်ခြင်း"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်သည် <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို ဖွင့်လိုက်ပါသည်"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်သည် <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို ပိတ်လိုက်ပါသည်"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"လက်ရှိ အသုံးလွယ်ရေး ဝန်ဆောင်မှုကို စတင်ရန် အသုံးလွယ်ရေး ဖြတ်လမ်းလင့်ခ်ကို သုံးပါ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"အများသုံးစွဲနိုင်မှု ခလုတ်ကို တို့သည့်အခါ အသုံးပြုမည့် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ−"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ဝန်ဆောင်မှုများကို ပြောင်းလဲရန် အများသုံးစွဲနိုင်မှု ခလုတ်ကို တို့၍ ထိထားပါ။"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ချဲ့ခြင်း"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 439b0de..28e7d33 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Problem med å registrere Wi-Fi-anrop med operatøren din: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi-anrop"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi-anrop"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-anrop"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN-anrop"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Maskinvare for fingeravtrykk er ikke tilgjengelig."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrykket kan ikke lagres. Fjern et eksisterende fingeravtrykk."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tidsavbrudd for fingeravtrykk er nådd. Prøv på nytt."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Tidsavbrudd for ansikt er nådd. Prøv igjen."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Ansiktet kan ikke lagres."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Ansikt-operasjonen ble avbrutt."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Ansiktsautentiseringen ble avbrutt av brukeren."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"For mange forsøk. Prøv igjen senere."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"For mange forsøk. Ansiktsautentisering er slått av."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Prøv igjen."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Fargekorrigering"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Snarveien for tilgjengelighet slo på <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Snarveien for tilgjengelighet slo av <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Bruk tilgjengelighetssnarveien igjen for å starte den nåværende tilgjengelighetsfunksjonen"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Velg en funksjon du vil bruke når du trykker på Tilgjengelighet-knappen:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"For å endre funksjoner, trykk på og hold inne Tilgjengelighet-knappen."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Forstørring"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index d45e28b..875f87d 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"तपाईंको सेवा प्रदायकमार्फत Wi-Fi कलिङ सुविधा दर्ता गर्ने क्रममा देखिएको समस्या: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi कलिङ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi कलिङ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN कल"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN कल"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"औँठाछाप भण्डारण गर्न सकिँदैन। कृपया अवस्थित औठाछाप हटाउनुहोस्।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"अनुहारको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"अनुहार भण्डारण गर्न सकिँदैन।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"अनुहार पहिचान रद्द गरियो।"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"प्रयोगकर्ताले अनुहार प्रमाणीकरण रद्द गर्नु भयो।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"धेरैपटक प्रयासहरू भए। पछि फेरि प्रयास गर्नुहोस्।"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"अत्यधिक धेरैपटक गलत प्रयासहरू भए। अनुहार प्रमाणिकरणलाई असक्षम पारियो।"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"फेरि प्रयास गर्नुहोस्।"</string>
@@ -1622,6 +1629,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"रङ सच्याउने सुविधा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"पहुँचको सर्टकटले <xliff:g id="SERVICE_NAME">%1$s</xliff:g> लाई सक्रिय पार्यो"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"पहुँचको सर्टकटले <xliff:g id="SERVICE_NAME">%1$s</xliff:g> लाई निष्क्रिय पार्यो"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"हालको पहुँचसम्बन्धी सुविधा प्रयोग गर्न फेरि पहुँचसम्बन्धी सर्टकट प्रयोग गर्नुहोस्"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"तपाईंले पहुँच सम्बन्धी बटनलाई ट्याप गर्दा प्रयोग गर्नुपर्ने सुविधा रोज्नुहोस्:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"सुविधाहरूलाई बदल्न, पहुँच सम्बन्धी बटनलाई छोएर थिची राख्नुहोस्।"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"म्याग्निफिकेसन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index df152b2..b95ba19 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -79,7 +79,7 @@
<string name="CLIRPermanent" msgid="3377371145926835671">"U kunt de instelling voor de beller-ID niet wijzigen."</string>
<string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Geen service voor mobiele data"</string>
<string name="RestrictedOnEmergencyTitle" msgid="6855466023161191166">"Noodoproepen niet beschikbaar"</string>
- <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Geen service voor spraakoproepen"</string>
+ <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Geen belservice"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="8037246983606545202">"Geen spraakservice of noodoproepen"</string>
<string name="RestrictedStateContent" msgid="6538703255570997248">"Tijdelijk uitgeschakeld door je provider"</string>
<string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"Tijdelijk uitgeschakeld door je provider voor sim <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Probleem bij registratie van Bellen via wifi bij je provider: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Bellen via wifi van %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Bellen via wifi van <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Bellen via WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Bellen via WLAN van <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware voor vingerafdruk niet beschikbaar."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan niet worden opgeslagen. Verwijder een bestaande vingerafdruk."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Time-out bereikt voor vingerafdruk. Probeer het opnieuw."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Time-out voor gezicht bereikt. Probeer opnieuw."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Gezicht kan niet worden opgeslagen."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Bewerking voor gezichtsherkenning geannuleerd."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Gezichtsverificatie geannuleerd door gebruiker."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogingen. Probeer het later opnieuw."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Te veel pogingen. Gezichtsherkenning inactief."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Probeer het opnieuw."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurcorrectie"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ingeschakeld"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gebruik de snelkoppeling voor toegankelijkheid nogmaals om de huidige toegankelijkheidsfunctie te starten"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Kies een functie om te gebruiken wanneer je op de knop Toegankelijkheid tikt:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Als je functies wilt wijzigen, tik je op de knop Toegankelijkheid en houd je deze vast."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Vergroting"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 78566da..9d3fa8e 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"ଆପଣଙ୍କ କେରିଅର୍ ସହିତ ୱାଇ-ଫାଇ କଲ୍ କରିବା ପାଇଁ ପଞ୍ଜୀକରଣ କରିବାରେ ସମସ୍ୟା: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s ୱାଇ-ଫାଇ କଲିଙ୍ଗ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ୱାଇ-ଫାଇ କଲିଂ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN କଲ୍"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN କଲ୍"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଷ୍ଟୋର୍ କରାଯାଇପାରିବ ନାହିଁ। ଦୟାକରି ପୂର୍ବରୁ ଥିବା ଆଙ୍ଗୁଠି ଚିହ୍ନକୁ ବାହାର କରିଦିଅନ୍ତୁ।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ଆଙ୍ଗୁଠି ଚିହ୍ନର ସମୟ ଶେଷ ହେଲା । ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ଫେସ୍ର ସମୟସୀମା ସରିଗଲା। ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ଫେସ୍ ମେମୋରୀରେ ଷ୍ଟୋର୍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ଫେସ୍ର ଅପରେଶନ୍ କ୍ୟାନ୍ସଲ୍ ହୋଇଗଲା"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ଉପଯୋଗକର୍ତ୍ତା ମୁହଁ ଚିହ୍ନଟକରଣ ବାତିଲ୍ କରିଛନ୍ତି।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ବାରମ୍ବାର ଚେଷ୍ଟା। ପରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ବାରମ୍ବାର ଚେଷ୍ଟା। ଫେସ୍ ପ୍ରମାଣୀକରଣ ଅକ୍ଷମ କରାଗଲା।"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ଆକ୍ସେସିବିଲିଟୀ ଶର୍ଟକଟ୍ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଅନ୍ କରାଯାଇଛି"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ଆକ୍ସେସିବିଲିଟୀ ଶର୍ଟକଟ୍ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଅଫ୍ କରାଯାଇଛି"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ବୈଶିଷ୍ଟ୍ୟ ଆରମ୍ଭ କରିବାକୁ ପୁଣି ଆକ୍ସେସିବିଲିଟୀ ସର୍ଟ୍କର୍ଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ଆପଣ ଆକ୍ସେସବିଲିଟି ବଟନ୍ ଟାପ୍ କରିବା ବେଳେ ଏକ ବୈଶିଷ୍ଟ୍ୟ ବ୍ୟବହାର କରିବାକୁ ବାଛନ୍ତୁ:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ବୈଶିଷ୍ଟ୍ୟ ବଦଳାଇବାକୁ, ଆକ୍ସେସବିଲିଟି ବଟନ୍ ସ୍ପର୍ଶ କରନ୍ତୁ ଓ ଧରିରଖନ୍ତୁ।"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ମ୍ୟାଗ୍ନିଫିକେସନ୍"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index bb38c88..6e615a8 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"ਤੁਹਾਡੇ ਕੈਰੀਅਰ ਨਾਲ ਵਾਈ-ਫਾਈ ਕਾਲਿੰਗ ਨੂੰ ਰਜਿਸਟਰ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s ਵਾਈ-ਫਾਈ ਕਾਲਿੰਗ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ਵਾਈ-ਫਾਈ ਕਾਲਿੰਗ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN ਕਾਲ"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN ਕਾਲ"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਮੌਜੂਦਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਟਾਓ।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ਚਿਹਰਾ ਪਛਾਣਨ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ਚਿਹਰੇ ਦੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ਚਿਹਰਾ ਪਛਾਣਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ ਨੂੰ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਬੰਦ ਹੈ।"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"ਰੰਗ ਸੁਧਾਈ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਨੇ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਨੇ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ਮੌਜੂਦਾ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ \'ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ\' ਦੀ ਦੁਬਾਰਾ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਟੈਪ ਕੀਤੇ ਜਾਣ \'ਤੇ ਵਰਤਣ ਲਈ ਕੋਈ ਵਿਸ਼ੇਸ਼ਤਾ ਚੁਣੋ:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ।"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"ਵੱਡਦਰਸ਼ੀਕਰਨ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d59b138..81a869a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Podczas rejestrowania połączeń przez Wi-Fi u operatora wystąpił problem: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Połączenia przez Wi-Fi (%s)"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g>, połączenia przez Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Połączenie przez WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g>, połączenie przez WLAN"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Czytnik linii papilarnych nie jest dostępny."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Nie można zapisać odcisku palca. Usuń istniejący odcisk palca."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Osiągnięto limit czasu odczytu linii papilarnych. Spróbuj ponownie."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Upłynął limit czasu analizy twarzy. Spróbuj ponownie."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Nie można zapisać informacji o twarzy."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Analiza twarzy została anulowana."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Użytkownik anulował uwierzytelnianie twarzą."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Zbyt wiele prób. Spróbuj ponownie później."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zbyt wiele prób. Wyłączono uwierzytelnianie za pomocą twarzy."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Spróbuj ponownie."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcja kolorów"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Użyj ponownie skrótu ułatwień dostępu, by uruchomić bieżące ułatwienie dostępu"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Wybierz funkcję używaną po kliknięciu przycisku ułatwień dostępu."</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Aby zmienić funkcje, kliknij i przytrzymaj przycisk ułatwień dostępu."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Powiększenie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 48302a0..5ac7c2e5 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Ocorreu um problema ao registrar a chamada no Wi‑Fi junto à sua operadora: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s chamada Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Chamada no Wi-Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Chamada por WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Chamada por WLAN de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não foi possível armazenar a impressão digital. Remova uma impressão digital já existente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Tempo máx. p/ captura facial atingido. Tente novamente."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar um rosto."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autenticação facial cancelada pelo usuário."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Excesso de tentativas. Autenticação facial desat."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correção de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atalho de acessibilidade ativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use o atalho de acessibilidade novamente para iniciar o recurso de acessibilidade atual"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha um recurso a ser usado ao tocar no botão Acessibilidade:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para alterar os recursos, mantenha o botão Acessibilidade pressionado."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliação"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3f02828..52f067f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Ocorreu um problema ao registar a funcionalidade Chamadas Wi-Fi junto do seu operador: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Chamadas por Wi-Fi da %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Chamadas Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Chamada WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Chamada WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -419,11 +423,11 @@
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas definições de áudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que a aplicação modifique definições de áudio globais, tais como o volume e qual o altifalante utilizado para a saída de som."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
- <string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta aplicação pode gravar áudio através do microfone a qualquer momento."</string>
+ <string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta aplicação pode gravar áudio através do microfone em qualquer altura."</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que a aplicação envie comandos para o SIM. Esta ação é muito perigosa."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e vídeos"</string>
- <string name="permdesc_camera" msgid="5392231870049240670">"Esta aplicação pode tirar fotos e gravar vídeos através da câmara a qualquer momento."</string>
+ <string name="permdesc_camera" msgid="5392231870049240670">"Esta aplicação pode tirar fotos e gravar vídeos através da câmara em qualquer altura."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Permite à aplicação controlar o vibrador."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"marcar números de telefone diretamente"</string>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não é possível armazenar a impressão digital. Remova uma impressão digital existente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Foi atingido o limite de tempo da impressão digital. Tente novamente."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Limite de tempo de rosto atingido. Tente novamente."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar o rosto."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operação de rosto cancelada."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autenticação facial cancelada pelo utilizador."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiadas tentativas. Tente novamente mais tarde."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiadas tentativas. Autenticação facial desativada."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correção da cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O Atalho de acessibilidade ativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O Atalho de acessibilidade desativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Utilize novamente o atalho de acessibilidade para iniciar a funcionalidade de acessibilidade atual."</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para alterar as funcionalidades, toque sem soltar no botão Acessibilidade."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliação"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 48302a0..5ac7c2e5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Ocorreu um problema ao registrar a chamada no Wi‑Fi junto à sua operadora: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s chamada Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Chamada no Wi-Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Chamada por WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Chamada por WLAN de <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não foi possível armazenar a impressão digital. Remova uma impressão digital já existente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Tempo máx. p/ captura facial atingido. Tente novamente."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar um rosto."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autenticação facial cancelada pelo usuário."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Excesso de tentativas. Autenticação facial desat."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Correção de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atalho de acessibilidade ativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Use o atalho de acessibilidade novamente para iniciar o recurso de acessibilidade atual"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha um recurso a ser usado ao tocar no botão Acessibilidade:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Para alterar os recursos, mantenha o botão Acessibilidade pressionado."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ampliação"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 1c95559..0c945f7 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -129,10 +129,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"A apărut o problemă la înregistrarea apelării prin Wi‑Fi la operatorul dvs.: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Apelare prin Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Apelare prin Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Apel WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Apel WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -527,6 +531,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware-ul pentru amprentă nu este disponibil."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Amprenta nu poate fi stocată. Eliminați o amprentă existentă."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timpul pentru amprentare a expirat. Încercați din nou."</string>
@@ -563,6 +569,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Timpul pentru reunoaștere facială a expirat. Încercați din nou."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Chipul nu poate fi stocat."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operațiunea privind chipul a fost anulată."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autentificarea chipului anulată de utilizator."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Prea multe încercări. Reîncercați mai târziu."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Prea multe încercări. Autentificarea facială este dezactivată."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Încercați din nou."</string>
@@ -1640,6 +1647,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Corecția culorii"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Comanda rapidă de accesibilitate a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Comanda rapidă de accesibilitate a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Folosiți din nou comanda rapidă Accesibilitate pentru a porni funcția de accesibilitate prezentă"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Alegeți o funcție pe care să o folosiți când atingeți butonul Accesibilitate:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Pentru a schimba funcțiile, atingeți lung butonul Accesibilitate."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Mărire"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index c14bce4..fd70e9c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Ошибка подключения функции \"Звонки по Wi-Fi\" через вашего оператора: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Звонки по Wi-Fi (%s)"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Звонки по Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Вызов WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> Вызов WLAN"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Сканер недоступен"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Чтобы сохранить новый отпечаток, удалите существующий."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Превышено время ожидания. Повторите попытку."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Превышено время ожидания. Повторите попытку."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Невозможно сохранить распознанное лицо"</string>
<string name="face_error_canceled" msgid="283945501061931023">"Распознавание отменено"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Распознавание лица отменено пользователем."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Слишком много попыток. Повторите позже."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Слишком много попыток. Сканер отключен."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Попробуйте ещё раз"</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Коррекция цвета"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> включен"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> отключен"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Чтобы активировать выбранные специальные возможности, воспользуйтесь быстрым включением ещё раз"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Выберите функцию, которая запускается при нажатии кнопки специальных возможностей:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Чтобы изменить функцию, удерживайте кнопку специальных возможностей."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Увеличение"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 0f353f2..3bd8ceb 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"ඔබගේ වාහකය සමඟ Wi-Fi ඇමතුම් ලියාපදිංචි කිරීම නිකුත් කරන්න: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi අමතමින්"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi ඇමතුම්"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN ඇමතුම"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN ඇමතුම්"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ඇඟිලි සලකුණ ගබඩා කළ නොහැක. දැනට පවතින ඇඟිලි සලකුණක් ඉවත් කරන්න."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ඇඟිලි සලකුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"මුහුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"මුහුණ ගබඩා කළ නොහැක."</string>
<string name="face_error_canceled" msgid="283945501061931023">"මුහුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"පරිශීලකයා විසින් මුහුණ සත්යාපනය අවලංගු කරන ලදී."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"උත්සාහයන් ඉතා වැඩි ගණනකි. පසුව නැවත උත්සාහ කරන්න."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"උත්සාහයන් ඉතා වැඩි ගණනකි. මුහුණු සත්යාපනය අබල කරන ලදී."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"නැවත උත්සාහ කරන්න."</string>
@@ -1618,6 +1625,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"වර්ණ නිවැරදි කිරීම"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ප්රවේශ්යතා කෙටි මග <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මක කරන ලදී"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ප්රවේශ්යතා කෙටි මග <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිත කරන ලදී"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"වර්තමාන ප්රවේශ්යතා විශේෂංගය ආරම්භ කිරීම සඳහා ප්රවේශ්යතා කෙටි මග භාවිතා කරන්න"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ඔබ ප්රවේශ්යතා බොත්තම තට්ටු කරන විට භාවිතා කිරීමට අංගයක් තෝරාගන්න:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"අංග වෙනස් කිරීමට ප්රවේශ්යතා බොත්තම ස්පර්ශ කර අල්ලා ගෙන සිටින්න."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"විශාලනය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 3109702..c0981ce 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Pri registrácii volania cez Wi‑Fi u operátora nastala chyba <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Volanie siete Wi‑Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> volanie cez Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Volanie cez WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> volanie cez WLAN"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardvér na snímanie odtlačku prsta nie je k dispozícii"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Odtlačok prsta nie je možné uložiť. Odstráňte existujúci odtlačok."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit rozpoznania odtlačku vypršal. Skúste to znova."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Limit rozpoznania tváre vypršal. Skúste to znova."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Tvár sa nedá uchovať."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operácia týkajúca sa tváre bola zrušená"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Overenie tváre bolo zrušené používateľom."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Príliš veľa pokusov. Overenie tváre je zakázané."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Skúste to znova."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Úprava farieb"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skratka dostupnosti zapla službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skratka dostupnosti vypla službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Znova použite skratku dostupnosti, čím sprístupníte aktuálnu funkciu dostupnosti"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Klepnutím na tlačidlo dostupnosti vyberte požadovanú funkciu:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Ak chcete zmeniť funkcie, klepnite na tlačidlo dostupnosti a podržte ho"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Priblíženie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4575d00..261e6ed 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Težava pri registriranju klicanja prek Wi-Fi-ja pri operaterju: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Klicanje prek Wi-Fi-ja (%s)"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Klicanje prek Wi-Fi-ja operaterja <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Klic prek omrežja WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Klic prek omrežja WLAN operaterja <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Strojna oprema za prstne odtise ni na voljo."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Prstnega odtisa ni mogoče shraniti. Odstranite obstoječi prstni odtis."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Dosežena časovna omejitev za prstni odtis. Poskusite znova."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Dosežena časovna omejitev za obraz. Poskusite znova."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Obraza ni mogoče shraniti."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Dejanje z obrazom je bilo preklicano."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Preverjanje pristnosti obraza preklical uporabnik"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Preveč poskusov. Poskusite znova pozneje."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Preveč poskusov. Preverjanje pristnosti obraza je onemogočeno."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Poskusite znova."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Popravljanje barv"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Bližnjica funkcij za ljudi s posebnimi potrebami je vklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Bližnjica funkcij za ljudi s posebnimi potrebami je izklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Če želite zagnati trenutno funkcijo za ljudi s posebnimi potrebami, znova uporabite bližnjico funkcij za ljudi s posebnimi potrebami"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izberite funkcijo, ki jo želite uporabljati, ko se dotaknete gumba »Dostopnost«:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Če želite spremeniti funkcije, se dotaknite gumba »Dostopnost« in ga pridržite."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Povečava"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 1149421..4b6cb09 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Problem gjatë regjistrimit të telefonatave me Wi‑Fi me operatorin tënd celular: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Telefonatat me Wi-Fi nga %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Telefonatë me Wi-Fi në <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Telefonatë me WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Telefonatë me WLAN në <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardueri i gjurmës së gishtit nuk mundësohet."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Gjurma e gishtit nuk mund të ruhet. Hiq një gjurmë gishti ekzistuese."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Koha e veprimit për gjurmën e gishtit skadoi. Provo përsëri."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Mbaroi afati për fytyrën. Provo sërish."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Fytyra nuk mund të ruhet."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Veprimi me fytyrën u anulua."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Vërtetimi me fytyrë u anulua nga përdoruesi."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Shumë përpjekje. Provo sërish më vonë."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Shumë përpjekje. Vërtetimi për fytyrën joaktiv."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Provo sërish."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Korrigjimi i ngjyrës"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Shkurtorja e qasshmërisë e aktivizoi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Shkurtorja e qasshmërisë e çaktivizoi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Përdor përsëri \"Shkurtoren e qasshmërisë\" për të nisur funksionin aktual të qasshmërisë"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Zgjidh një funksion për ta përdorur kur troket butonin e \"Qasshmërisë\":"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Për të ndryshuar funksionet, prek dhe mbaj butonin e \"Qasshmërisë\"."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zmadhimi"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7536531..449fdfc 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -129,10 +129,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Проблем у вези са регистровањем позивања преко Wi‑Fi-ја код мобилног оператера: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Wi-Fi позивање преко оператера %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> позивање преко Wi-Fi-ја"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN позив"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN позив"</string>
+ <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>
<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>
@@ -527,6 +531,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардвер за отиске прстију није доступан."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Није могуће сачувати отисак прста. Уклоните неки од постојећих отисака прстију."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Временско ограничење за отисак прста је истекло. Пробајте поново."</string>
@@ -563,6 +569,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Истекло је време за проверу лица. Пробајте поново."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Није могуће сачувати лице."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Обрада лица је отказана."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Корисник је отказао потврду лица."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Превише покушаја. Пробајте поново касније."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Више покушаја. Потврда идентитета је онемогућена."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Пробајте поново."</string>
@@ -1640,6 +1647,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Корекција боја"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Пречица за приступачност је укључила услугу <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Пречица за приступачност је искључила услугу <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Употребите поново пречицу за приступачност да бисте покренули актуелну функцију приступачности"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изаберите функцију која ће се користити када додирнете дугме за приступачност:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Притисните и задржите дугме за приступачност да бисте мењали функције."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Увећање"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 31bb349..9f46514 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Följande fel uppstod när Wi-Fi-samtal skulle registreras hos operatören: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi-samtal"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Wi-Fi-samtal via <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN-samtal"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"WLAN-samtal via <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Det finns ingen maskinvara för fingeravtryck."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrycket kan inte lagras. Ta bort ett befintligt fingeravtryck."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tidsgränsen för fingeravtrycket har uppnåtts. Försök igen."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Tidsgränsen för ansikte har nåtts. Försök igen."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Det gick inte att lagra ansiktet."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Ansiktsåtgärden har avbrutits."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Autentiseringen av ansiktet avbröts av användaren."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Du har gjort för många försök. Försök igen senare."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"För många försök. Ansiktsautentisering inaktiverad"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Försök igen."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Färgkorrigering"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiverades av Aktivera tillgänglighet snabbt"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> inaktiverades av Aktivera tillgänglighet snabbt"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Använd Aktivera tillgänglighet snabbt en gång till om du vill aktivera tillgänglighetsfunktionen i fråga"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Välj en funktion som ska användas när du trycker på tillgänglighetsknappen:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Tryck länge på tillgänglighetsknappen för att ändra funktioner."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Förstoring"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 3d26224..b0858dc 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -128,8 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Tatizo limetokea wakati wa kuisajili huduma ya kupiga simu kupitia Wi‑Fi kwa mtoa huduma wako: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <!-- String.format failed for translation -->
- <!-- no translation found for wfcSpnFormats:0 (6830082633573257149) -->
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Kupiga Simu Kupitia Wi-Fi ya <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Simu ya WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Simu ya WLAN ya <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -522,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Maunzi ya kitambulisho hayapatikani."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Kitambulisho hakiwezi kuhifadhiwa. Tafadhali ondoa kitambulisho kilichopo."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Muda wa kitambulisho umekwisha. Jaribu tena."</string>
@@ -558,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Muda wa kutambua uso umeisha. Jaribu tena."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Huwezi kuhifadhi uso."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Utendaji wa kitambulisho umeghairiwa."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Uthibitishaji wa uso umeghairiwa na mtumiaji."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Umejaribu mara nyingi mno. Jaribu tena baadaye."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Umejaribu mara nyingi mno. Kitambuzi cha uso kimezimwa."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Jaribu tena."</string>
@@ -1614,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Usahihishaji wa rangi"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Tumia njia ya Mkato wa Zana za walio na matatizo ya kuona au kusikia tena ili kuanzisha kipengele kilichopo cha walio na matatizo ya kuona au kusikia"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Chagua kipengele utakachotumia, ukigonga Kitufe cha zana za walio na matatizo ya kuona au kusikia."</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Ili kubadilisha vipengele, gusa na ushikile Kitufe cha zana za walio na matatizo ya kuona au kusikia."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ukuzaji"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 35592a6..b84d68e 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"உங்கள் மொபைல் நிறுவனத்துடன் வைஃபை அழைப்பைப் பதிவுசெய்வதில் சிக்கல்: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s வைஃபை அழைப்பு"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> வைஃபை அழைப்பு"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN அழைப்பு"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN அழைப்பு"</string>
+ <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>
<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>
@@ -514,8 +518,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"உங்களின் படத் தொகுப்பை மாற்ற ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"மீடியா தொகுப்பிலிருந்து இடங்களை அறிதல்"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"உங்களின் மீடியா தொகுப்பிலிருந்து இடங்களை அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கும்."</string>
- <!-- no translation found for biometric_error_hw_unavailable (645781226537551036) -->
- <skip />
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string>
<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>
@@ -523,9 +526,10 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"விரலை மிகவும் மெதுவாக நகர்த்திவிட்டீர்கள். மீண்டும் முயற்சிக்கவும்."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <!-- no translation found for biometric_not_recognized (5770511773560736082) -->
- <skip />
+ <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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"கைரேகை வன்பொருள் இல்லை."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"கைரேகையைச் சேமிக்க முடியவில்லை. ஏற்கனவே உள்ள கைரேகையை அகற்றவும்."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
@@ -562,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"முகப் பதிவிற்கான நேரம் முடிந்தது. மீண்டும் முயல்க."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"முகத்தைச் சேமிக்க இயலாது."</string>
<string name="face_error_canceled" msgid="283945501061931023">"முக அங்கீகாரச் செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"பயனர், முக அங்கீகாரத்தை ரத்துசெய்தார்."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"பலமுறை தோல்வி. முக அங்கீகாரம் முடக்கப்பட்டது."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"மீண்டும் முயலவும்."</string>
@@ -1619,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"வண்ணத் திருத்தம்"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"அணுகல்தன்மைக் குறுக்குவழியானது <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐ இயக்கியது"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"அணுகல்தன்மைக் குறுக்குவழியானது <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐ முடக்கியது"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"தற்போதுள்ள அணுகல்தன்மை அம்சத்தை மீண்டும் தொடங்க ’அணுகல்தன்மை ஷார்ட்கட்டைப்’ பயன்படுத்தவும்"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"அணுகல்தன்மைப் பொத்தானைத் தட்டி, பயன்படுத்துவதற்கான அம்சத்தைத் தேர்ந்தெடுக்கவும்:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"அம்சங்களை மாற்ற, அணுகல்தன்மைப் பொத்தானைத் தொட்டுப் பிடித்திருக்கவும்."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"பெரிதாக்கல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 2027d33..a5430b6 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"మీ క్యారియర్తో Wi‑Fi కాలింగ్ని నమోదు చేయడంలో సమస్య: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi కాలింగ్"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi కాలింగ్"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN కాల్"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN కాల్"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"వేలిముద్ర హార్డ్వేర్ అందుబాటులో లేదు."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"వేలిముద్రను నిల్వ చేయడం సాధ్యపడదు. దయచేసి ఇప్పటికే ఉన్న వేలిముద్రను తీసివేయండి."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"ముఖ గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"ముఖం నిల్వ చేయబడదు."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ముఖ కార్యకలాపం రద్దయింది."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"వినియోగదారు ద్వారా ముఖ ప్రమాణీకరణ రద్దు చేయబడింది."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. ముఖ ప్రమాణీకరణ నిలిపివేయబడింది."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"మళ్లీ ప్రయత్నించండి."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"రంగు సవరణ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"యాక్సెస్ సామర్థ్య షార్ట్కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"యాక్సెస్ సామర్థ్య షార్ట్కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ప్రస్తుత యాక్సెస్ సౌలభ్య ఫీచర్ను ఉపయోగించడానికి యాక్సెసిబిలిటీ షార్ట్కట్ను మళ్లీ ప్రారంభించండి"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"యాక్సెస్ సామర్థ్య బటన్ను మీరు నొక్కినప్పుడు ఉపయోగించాల్సిన ఒక ఫీచర్ను ఎంచుకోండి:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"ఫీచర్లను మార్చడానికి, యాక్సెస్ సామర్థ్య బటన్ను నొక్కి & పట్టుకోండి."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"మాగ్నిఫికేషన్"</string>
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 0712cbc..48b59c7 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -15,7 +15,6 @@
-->
<resources>
<style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
- <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
<style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index 293591a..fdd0624 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -15,8 +15,20 @@
-->
<resources>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
- <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill" />
- <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save" />
+
+ <!-- TODO(b/116457731): remove colorBackground from colors_material.xml if not used anymore -->
+ <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material">
+ <item name="colorBackground">@color/autofill_background_material_dark</item>
+ </style>
+ <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Panel">
+ <item name="colorBackground">@color/autofill_background_material_dark</item>
+ </style>
+
+ <!-- TV always use dark mode -->
+ <style name="Theme.DeviceDefault.Autofill.Light" parent="Theme.DeviceDefault.Autofill"/>
+ <style name="Theme.DeviceDefault.Light.Autofill.Save" parent="Theme.DeviceDefault.Autofill.Save"/>
+
<style name="Theme.DeviceDefault.Resolver" parent="Theme.Leanback.Resolver" />
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 84fd018..0b7e031 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"เกิดปัญหาในการลงทะเบียนการโทรผ่าน Wi‑Fi กับผู้ให้บริการของคุณ: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"กำลังเรียก Wi-Fi ของ %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"การโทรผ่าน Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"การโทรผ่าน WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"การโทรผ่าน WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ฮาร์ดแวร์ลายนิ้วมือไม่พร้อมใช้งาน"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ไม่สามารถเก็บลายนิ้วมือได้ โปรดนำลายนิ้วมือที่มีอยู่ออก"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"หมดเวลาใช้ลายนิ้วมือแล้ว โปรดลองอีกครั้ง"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"หมดเวลาใช้ใบหน้าแล้ว โปรดลองอีกครั้ง"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"จัดเก็บข้อมูลใบหน้าไม่ได้"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ยกเลิกการดำเนินการกับใบหน้าแล้ว"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"ผู้ใช้ยกเลิกการตรวจสอบสิทธิ์ใบหน้า"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"ดำเนินการหลายครั้งเกินไป ปิดใช้การตรวจสอบสิทธิ์ด้วยใบหน้าแล้ว"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"ลองอีกครั้ง"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"การปรับแก้สี"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ทางลัดการเข้าถึงเปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ทางลัดการเข้าถึงปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"ใช้ทางลัดการช่วยเหลือพิเศษอีกครั้งเพื่อเริ่มฟีเจอร์การช่วยเหลือพิเศษปัจจุบัน"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"เลือกฟีเจอร์ที่จะใช้เมื่อคุณแตะปุ่ม \"การเข้าถึงพิเศษ\":"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"หากต้องการเปลี่ยนฟีเจอร์ ให้แตะปุ่ม \"การเข้าถึงพิเศษ\" ค้างไว้"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"การขยาย"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 49318cf..e21f59c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Nagkaroon ng isyu sa pagrehistro ng pagtawag gamit ang Wi‑Fi sa iyong carrier: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Pagtawag sa Pamamagitan ng Wi-Fi ng %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Pagtawag Gamit ang Wi-Fi ng <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Pagtawag Gamit ang WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Pagtawag Gamit ang WLAN ng <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hindi available ang hardware na ginagamitan ng fingerprint."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Hindi maiimbak ang fingerprint. Mangyaring mag-alis ng umiiral nang fingerprint."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Nag-time out ang fingerprint. Subukang muli."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Nag-time out ang mukha. Subukang muli."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Hindi ma-store ang mukha."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Nakansela ang operation kaugnay ng mukha."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Kinansela ng user ang pag-authenticate ng mukha."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Masyadong maraming pagsubok. Subukang muli mamaya."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Sobrang pagsubok. Bawal na: facial authentication."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Subukang muli."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Pagwawasto ng Kulay"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Na-on ng Shortcut sa Accessibility ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Na-off ng Shortcut sa Accessibility ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Gamiting muli ang Shortcut sa Pagiging Naa-access para simulan ang kasalukuyang feature ng pagiging naa-access"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pumili ng feature na gagamitin kapag na-tap mo ang button ng Pagiging Naa-access:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Upang baguhin ang mga feature, pindutin nang matagal ang button ng Pagiging Naa-access."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Pag-magnify"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index c6d18ec..912db20 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Kablosuz çağrının operatörünüze kaydı sırasında hata oluştu: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Kablosuz Çağrı"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Kablosuz Çağrı"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN Üzerinden Çağrı"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN Üzerinden Çağrı"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Parmak izi donanımı kullanılamıyor."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Yüz için zaman aşımı oluştu. Tekrar deneyin."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Yüz kaydedilemiyor."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Yüz işlemi iptal edildi."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Yüz kimlik doğrulama işlemini kullanıcı iptal etti."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Çok fazla deneme yapıldı. Yüz kimlik doğrulaması devre dışı bırakıldı."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Tekrar deneyin."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Renk Düzeltme"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Erişilebilirlik Kısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini açtı"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Erişilebilirlik Kısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kapattı"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Geçerli erişilebilirlik özelliğini başlatmak için Erişilebilirlik Kısayolu\'nu tekrar kullanın"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Erişilebilirlik düğmesine dokunduğunuzda kullanmak üzere bir özellik seçin:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Özellikleri değiştirmek için Erişilebilirlik düğmesine dokunup basılı tutun."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Büyütme"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6e6dd9a..f84e3e2 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -130,10 +130,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Проблема з реєстрацією дзвінків через Wi‑Fi у вашого оператора: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Дзвінок через Wi-Fi від оператора %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Виклики <xliff:g id="SPN">%s</xliff:g> через Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Виклик через WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Виклик <xliff:g id="SPN">%s</xliff:g> через WLAN"</string>
+ <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>
<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>
@@ -530,6 +534,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Апаратне забезпечення для сканування відбитка недоступне."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток. Видаліть наявний відбиток."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час очікування відбитка минув. Повторіть спробу."</string>
@@ -566,6 +572,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Час очікування обличчя минув. Повторіть спробу."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Не вдається зберегти обличчя."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Дію з обличчям скасовано."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Користувач скасував автентифікацію облич."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Забагато спроб. Повторіть пізніше."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Забагато спроб. Автентифікацію обличчя вимкнено."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Повторіть спробу."</string>
@@ -1664,6 +1671,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Корекція кольорів"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Ярлик спеціальних можливостей увімкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Ярлик спеціальних можливостей вимкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Щоб запустити поточну функцію спеціальних можливостей, знову скористайтеся відповідним ярликом"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Виберіть функцію для кнопки спеціальних можливостей:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Щоб змінити функцію, натисніть і втримуйте кнопку спеціальних можливостей."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Збільшення"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2a2ca62..dc801cd 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"آپ کے کیریئر کے ساتھ Wi‑Fi کالنگ رجسٹر کرنے میں مسئلہ درپیش ہے: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi کالنگ"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi کالنگ"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN کال"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN کال"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے۔"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"فنگر پرنٹ اسٹور نہیں کیا جا سکتا ہے۔ براہ کرم ایک موجودہ فنگر پرنٹ ہٹائیں۔"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"چہرہ پہچانے کی میعاد ختم ہو گئی۔ دوبارہ کوشش کریں۔"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"چہرے کو اسٹور نہیں کیا جا سکتا۔"</string>
<string name="face_error_canceled" msgid="283945501061931023">"چہرے پر ہونے والی کارروائی منسوخ ہو گئی۔"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"صارف نے چہرے کی تصدیق کو مسترد کر دیا۔"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"کافی زیادہ کوششیں کی گئیں۔ دوبارہ کوشش کریں۔"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"کافی زیادہ کوششیں کی گئیں۔ چہرے کی توثیق منسوخ ہو گئی۔"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"دوبارہ کوشش کریں۔"</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"رنگ کی تصحیح"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ایکسیسبیلٹی شارٹ کٹ نے <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آن کر دیا"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ایکسیسبیلٹی شارٹ کٹ نے <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آف کر دیا"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"موجودہ ایکسیسبیلٹی خصوصیت کو شروع کرنے کیلئے دوبارہ ایکسیسبیلٹی شارٹ کٹ استعمال کریں"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ایکسیسبیلٹی بٹن پر تھپتھپانے وقت استعمال کرنے کیلئے ایک خصوصیت چنیں:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"خصوصیات تبدیل کرنے کیلئے، ایکسیسبیلٹی بٹن ٹچ کریں اور دبائے رکھیں۔"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"میگنیفکیشن"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 7ffc7e7..e51b810 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Aloqa operatoringiz orqali Wi-Fi chaqiruv funksiyasinini ulashda xato: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi qo‘ng‘iroqlar"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi chaqiruv"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN chaqiruv"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN chaqiruv"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Barmoq izi skaneri ish holatida emas."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Barmoq izini saqlab bo‘lmadi. Mavjud barmoq izlaridan birini o‘chirib tashlang."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Barmoq izini aniqlash vaqti tugab qoldi. Qayta urinib ko‘ring."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Yuzni aniqlash vaqti tugadi. Qaytadan urining."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Aniqlangan yuz saqlanmadi."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Yuzni aniqlash bekor qilindi."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Yuz tekshiruvi bekor qilindi."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Juda ko‘p urinildi. Keyinroq qaytadan urining."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Juda ko‘p urinildi. Skaner faolsizlantirildi."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Qaytadan urining."</string>
@@ -1617,6 +1624,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Rangni tuzatish"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmati yoqildi"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmati o‘chirib qo‘yildi"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Joriy maxsus imkoniyatlar funksiyasini boshlash uchun tezkor ishga tushirishdan qayta foydalaning"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Maxsus imkoniyatlar tugmasi bosilganda ishga tushadigan funksiyani tanlang:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Funksiyalarni o‘zgartirish uchun Maxsus imkoniyatlar tugmasini bosib turing."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Kattalashtirish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a5f458b..4d8a20b 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Sự cố khi đăng ký dịch vụ gọi qua Wi‑Fi với nhà mạng của bạn: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"Gọi điện qua Wi-Fi %s"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"Gọi qua Wi-Fi <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Cuộc gọi qua WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"Cuộc gọi qua WLAN <xliff:g id="SPN">%s</xliff:g>"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Phần cứng vân tay không khả dụng."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Không thể lưu vân tay. Vui lòng xóa vân tay hiện có."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Đã hết thời gian chờ vân tay. Hãy thử lại."</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Đã hết thời gian chờ khuôn mặt. Hãy thử lại."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Không thể lưu khuôn mặt."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Đã hủy thao tác dùng khuôn mặt."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Người dùng đã hủy thao tác xác thực khuôn mặt."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Bạn đã thử quá nhiều lần. Hãy thử lại sau."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Đã thử quá nhiều lần. Đã tắt xác thực khuôn mặt."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Hãy thử lại."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Sửa màu"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Đã bật phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Đã tắt phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Sử dụng lại Phím tắt hỗ trợ tiếp cận để bắt đầu tính năng hỗ trợ tiếp cận hiện tại"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Chọn tính năng sẽ sử dụng khi bạn nhấn nút Trợ năng:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Để thay đổi các tính năng, hãy chạm và giữ nút Trợ năng."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Phóng to"</string>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
deleted file mode 100644
index 1be47ba..0000000
--- a/core/res/res/values-watch/themes.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
- <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
- <item name="windowContentTransitions">false</item>
- <item name="windowActivityTransitions">false</item>
- <item name="windowCloseOnTouchOutside">false</item>
- </style>
-</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index a7736e7c..bfba312 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -422,6 +422,13 @@
<item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
</style>
+ <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
+ <item name="windowContentTransitions">false</item>
+ <item name="windowActivityTransitions">false</item>
+ <item name="windowCloseOnTouchOutside">false</item>
+ </style>
+
<style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 966a5a5..609f9bcf 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"向您的运营商注册 WLAN 通话时遇到问题:<xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s WLAN 通话功能"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> WLAN 通话"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN 通话"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN 通话"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"指纹硬件无法使用。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"无法存储指纹。请移除一个现有的指纹。"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"指纹录入操作超时,请重试。"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"面孔处理操作超时,请重试。"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"无法存储面孔。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"面孔处理操作已取消。"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"用户已取消面孔验证。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"尝试次数过多,请稍后重试。"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"尝试次数过多,系统已停用人脸身份验证功能。"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"请重试。"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"无障碍快捷方式已开启<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"无障碍快捷方式已关闭<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"再次使用无障碍快捷方式即可启动目前设置的无障碍功能"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"选择按下“无障碍”按钮时要使用的功能:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"要更改指定的功能,请触摸并按住“无障碍”按钮。"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"放大功能"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 87eb41f..2e3c7d7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"向您的流動網絡供應商註冊 Wi-Fi 通話時發生問題:<xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi 通話"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN 通話"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN 通話"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"無法使用指紋軟件。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋無法儲存。請移除現有指紋。"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋已逾時。請再試一次。"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"臉孔操作已逾時,請再試一次。"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"無法儲存臉孔。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"臉孔操作已取消。"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"使用者已取消臉孔驗證。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"嘗試次數過多,臉孔驗證已停用。"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"請再試一次。"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"無障礙功能快速鍵已啟用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"無障礙功能快速鍵已停用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"再用一次無障礙功能捷徑,就可以啟用宜家設定咗嘅無障礙功能"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"請選擇輕按「無障礙功能」按鈕時使用的功能:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"如要變更功能,可按住「無障礙功能」按鈕。"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"放大"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index bd36ed6..9149f47 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"向你的電信業者註冊 Wi‑Fi 通話時發生問題:<xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s Wi-Fi 通話"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi 通話"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN 通話"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN 通話"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"指紋硬體無法使用。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"無法儲存指紋,請先移除現有指紋。"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋處理作業逾時,請再試一次。"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"臉孔處理作業逾時,請再試一次。"</string>
<string name="face_error_no_space" msgid="8224993703466381314">"無法儲存臉孔。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"臉孔處理作業已取消。"</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"使用者已取消臉孔驗證。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"嘗試次數過多,臉孔驗證功能已停用。"</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"請再試一次。"</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"協助工具捷徑啟用了「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"協助工具捷徑停用了「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"再次使用協助工具捷徑即可啟動目前設定的無障礙功能"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"輕觸 [協助工具] 按鈕後,選擇你想使用的功能:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"如要變更指派的功能,請按住 [協助工具] 按鈕。"</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"放大"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index fd6614ce..75709ca 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -128,10 +128,14 @@
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="7372514042696663278">"Inkinga yokubhalisa ukushaya kwe-Wi-Fi ngenkampani yakho yenethiwekhi: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
- <string-array name="wfcSpnFormats">
- <item msgid="6830082633573257149">"%s"</item>
- <item msgid="4397097370387921767">"%s ukushaya kwe-Wi-Fi"</item>
- </string-array>
+ <!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
+ <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ukushaya kwe-Wi-Fi"</string>
+ <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"Ikholi ye-WLAN"</string>
+ <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> ikholi ye-WLAN"</string>
+ <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>
<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>
@@ -524,6 +528,8 @@
</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>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Izingxenyekazi zekhompuyutha zezingxivizo zeminwe azitholakali."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Izigxivizo zeminwe azikwazi ukugcinwa. Sicela ususe izigxivizo zeminwe ezikhona."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
@@ -560,6 +566,7 @@
<string name="face_error_timeout" msgid="4014326147867150054">"Kufinyelelwe kusikhathi sokuvala sobuso. Zama futhi."</string>
<string name="face_error_no_space" msgid="8224993703466381314">"Ubuso abukwazi ukugcinwa."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Umsebenzi wobuso ukhanselwe."</string>
+ <string name="face_error_user_canceled" msgid="8943921120862164539">"Ukufakazela ubuqiniso kobuso kukhanselwe umsebenzisi"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Imizamo eminingi kakhulu. Ukufakazela ubuqiniso kobuso kukhutshaziwe."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Zama futhi."</string>
@@ -1616,6 +1623,7 @@
<string name="color_correction_feature_name" msgid="6779391426096954933">"Ukulungiswa kombala"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Isinqamuleli sokufinyelela sivule i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Isinqamuleli sokufinyelela sivale i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"Sebenzisa isinqamuleli sokufinyelela futhi ukuze uqale isici samanje sokufinyelela"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Khetha isici ozosisebenzisa uma uthepha inkinobho yokufinyelela:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Ukuze ushintshe izici, thinta uphinde ubambe inkinobho yokufinyelela."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Ukukhuliswa"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 65b8807..e0db946 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1425,7 +1425,7 @@
at {@link android.view.inputmethod.InputConnection#performEditorAction(int)
InputConnection.performEditorAction(int)}.
<p>Corresponds to
- {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. -->
+ {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_PREVIOUS}. -->
<flag name="flagNavigatePrevious" value="0x4000000" />
<!-- Used to specify that there is something
interesting that a forward navigation can focus on. This is like using
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3c0e51e..8ff29ba 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1102,6 +1102,10 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="isFeatureSplit" format="boolean" />
+ <!-- Flag to specify if this APK requires at least one split [either feature or
+ resource] to be present in order to function. Default value is false. -->
+ <attr name="isSplitRequired" format="boolean" />
+
<!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
{@code <application>} tag. If specified on the {@code <application>}
tag these will be considered defaults for all activities in the
@@ -1422,6 +1426,7 @@
<attr name="targetSandboxVersion" />
<attr name="compileSdkVersion" />
<attr name="compileSdkVersionCodename" />
+ <attr name="isSplitRequired" />
</declare-styleable>
<!-- The <code>application</code> tag describes application-level components
diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml
index 6053728..32671ac8 100644
--- a/core/res/res/values/colors_car.xml
+++ b/core/res/res/values/colors_car.xml
@@ -17,15 +17,271 @@
*/
-->
<resources>
- <!-- car support colors from
- https://cs.corp.google.com/android/frameworks/support/car/res/values/colors.xml -->
- <color name="car_user_switcher_user_image_bgcolor">@color/car_grey_50</color>
- <color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color>
- <color name="car_card_dark">@color/car_dark_blue_grey_700</color>
- <color name="car_body1_light">@color/car_grey_100</color>
+ <color name="car_background">@color/black</color>
- <color name="car_grey_50">#fffafafa</color>
- <color name="car_grey_900">#ff212121</color>
- <color name="car_dark_blue_grey_700">#ff172026</color>
- <color name="car_grey_100">#fff5f5f5</color>
+ <color name="car_colorPrimary">@color/car_grey_868</color>
+ <color name="car_colorSecondary">@color/car_grey_900</color>
+ <color name="car_colorPrimaryDark">@color/car_grey_958</color>
+
+
+ <!-- Various colors for text sizes. "Light" and "dark" here refer to the lighter or darker
+ shades. -->
+ <color name="car_title_light">@color/car_grey_100</color>
+ <color name="car_title_dark">@color/car_grey_900</color>
+ <color name="car_title">@color/car_title_light</color>
+
+ <color name="car_title2_light">@color/car_grey_100</color>
+ <color name="car_title2_dark">@color/car_grey_900</color>
+ <color name="car_title2">@color/car_title2_light</color>
+
+ <color name="car_headline1_light">@color/car_grey_100</color>
+ <color name="car_headline1_dark">@color/car_grey_800</color>
+ <color name="car_headline1">@color/car_headline1_light</color>
+
+ <color name="car_headline2_light">@color/car_grey_100</color>
+ <color name="car_headline2_dark">@color/car_grey_900</color>
+ <color name="car_headline2">@color/car_headline2_light</color>
+
+ <color name="car_headline3_light">@android:color/white</color>
+ <color name="car_headline3_dark">@color/car_grey_900</color>
+ <color name="car_headline3">@color/car_headline3_light</color>
+
+ <color name="car_headline4_light">@android:color/white</color>
+ <color name="car_headline4_dark">@android:color/black</color>
+ <color name="car_headline4">@color/car_headline4_light</color>
+
+ <color name="car_body1_light">@color/car_grey_100</color>
+ <color name="car_body1_dark">@color/car_grey_900</color>
+ <color name="car_body1">@color/car_body1_light</color>
+
+ <color name="car_body2_light">@color/car_grey_300</color>
+ <color name="car_body2_dark">@color/car_grey_700</color>
+ <color name="car_body2">@color/car_body2_light</color>
+
+ <color name="car_body3_light">@android:color/white</color>
+ <color name="car_body3_dark">@android:color/black</color>
+ <color name="car_body3">@color/car_body3_light</color>
+
+ <color name="car_body4_light">@android:color/white</color>
+ <color name="car_body4_dark">@android:color/black</color>
+ <color name="car_body4">@color/car_body4_light</color>
+
+ <color name="car_action1_light">@color/car_grey_900</color>
+ <color name="car_action1_dark">@color/car_grey_50</color>
+ <color name="car_action1">@color/car_action1_light</color>
+
+ <!-- The tinting colors to create a light- and dark-colored icon respectively. -->
+ <color name="car_tint_light">@color/car_grey_50</color>
+ <color name="car_tint_dark">@color/car_grey_900</color>
+
+ <!-- The tinting color for an icon. This icon is assumed to be on a light background. -->
+ <color name="car_tint">@color/car_tint_light</color>
+
+ <!-- An inverted tinting from car_tint. -->
+ <color name="car_tint_inverse">@color/car_tint_dark</color>
+
+ <!-- The color of the divider. The color here is a lighter shade. -->
+ <color name="car_list_divider_light">#1fffffff</color>
+
+ <!-- The color of the divider. The color here is a darker shade. -->
+ <color name="car_list_divider_dark">#1f000000</color>
+
+ <!-- The color of the dividers in the list. This color is assumed to be on a light colored
+ view. -->
+ <color name="car_list_divider">@color/car_list_divider_light</color>
+
+ <!-- A light and dark colored card. -->
+ <color name="car_card_light">@color/car_grey_50</color>
+ <color name="car_card_dark">@color/car_dark_blue_grey_700</color>
+
+ <!-- The default color of a card in car UI. -->
+ <color name="car_card">@color/car_card_dark</color>
+
+ <!-- The ripple colors. The "dark" and "light" designation here refers to the color of the
+ ripple itself. -->
+ <color name="car_card_ripple_background_dark">#8F000000</color>
+ <color name="car_card_ripple_background_light">#27ffffff</color>
+
+ <!-- The ripple color for a light colored card. -->
+ <color name="car_card_ripple_background">@color/car_card_ripple_background_light</color>
+
+ <!-- The ripple color for a dark-colored card. This color is the opposite of
+ car_card_ripple_background. -->
+ <color name="car_card_ripple_background_inverse">@color/car_card_ripple_background_dark</color>
+
+ <!-- The top margin before the start of content in an application. -->
+ <dimen name="app_header_height">96dp</dimen>
+
+ <!-- The lighter and darker color for the scrollbar thumb. -->
+ <color name="car_scrollbar_thumb_light">#99ffffff</color>
+ <color name="car_scrollbar_thumb_dark">#7f0b0f12</color>
+
+ <!-- The color of the scroll bar indicator in the PagedListView. This color is assumed to be on
+ a light-colored background. -->
+ <color name="car_scrollbar_thumb">@color/car_scrollbar_thumb_light</color>
+
+ <!-- The inverted color of the scroll bar indicator. This color is always the opposite of
+ car_scrollbar_thumb. -->
+ <color name="car_scrollbar_thumb_inverse">@color/car_scrollbar_thumb_dark</color>
+
+ <!-- The color of the seekbar track secondary progress in SeekbarListItem. -->
+ <color name="car_seekbar_track_secondary_progress">@color/car_grey_500</color>
+
+ <!-- The lighter and darker color for the seekbar track background. -->
+ <color name="car_seekbar_track_background_light">@color/car_grey_400</color>
+ <color name="car_seekbar_track_background_dark">@color/car_grey_700</color>
+ <!-- The color of the seekbar track background in SeekbarListItem. This color is assumed to be
+ on a light-colored background. -->
+ <color name="car_seekbar_track_background">@color/car_seekbar_track_background_dark</color>
+ <!-- background is car_grey_868 with .9 alpha -->
+ <color name="car_toast_background">#E6282a2d</color>
+
+ <!-- Misc colors -->
+ <color name="car_highlight_light">@color/car_teal_700</color>
+ <color name="car_highlight_dark">@color/car_teal_200</color>
+ <color name="car_highlight">@color/car_highlight_dark</color>
+ <color name="car_accent_light">@color/car_highlight_light</color>
+ <color name="car_accent_dark">@color/car_highlight_dark</color>
+ <color name="car_accent">@color/car_highlight_dark</color>
+
+ <color name="car_user_switcher_user_image_bgcolor">@color/car_grey_50</color>
+ <color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color>
+
+ <!-- Color palette for cars -->
+ <color name="car_grey_958">#ff0e1013</color>
+ <color name="car_grey_928">#ff17181b</color>
+ <color name="car_grey_900">#ff202124</color>
+ <color name="car_grey_868">#ff282a2d</color>
+ <color name="car_grey_846">#ff2e3234</color>
+ <color name="car_grey_800">#ff3c4043</color>
+ <color name="car_grey_772">#ff464a4d</color>
+ <color name="car_grey_746">#ff4d5256</color>
+ <color name="car_grey_700">#ff5f6368</color>
+ <color name="car_grey_600">#ff80868b</color>
+ <color name="car_grey_500">#ff9aa0a6</color>
+ <color name="car_grey_400">#ffbdc1c6</color>
+ <color name="car_grey_300">#ffdadce0</color>
+ <color name="car_grey_200">#ffe8eaed</color>
+ <color name="car_grey_100">#fff1f3f4</color>
+ <color name="car_grey_50">#fff8f9fa</color>
+
+ <color name="car_blue_900">#ff1d57a9</color>
+ <color name="car_blue_800">#ff2065bb</color>
+ <color name="car_blue_700">#ff2374ce</color>
+ <color name="car_blue_600">#ff2581df</color>
+ <color name="car_blue_500">#ff5195ea</color>
+ <color name="car_blue_400">#ff6ba5ed</color>
+ <color name="car_blue_300">#ff96bff2</color>
+ <color name="car_blue_200">#ffb9d4f6</color>
+ <color name="car_blue_100">#ffd9e6f9</color>
+ <color name="car_blue_50">#ffebf1fc</color>
+
+ <color name="car_green_900">#ff136e39</color>
+ <color name="car_green_800">#ff1b7e42</color>
+ <color name="car_green_700">#ff218c48</color>
+ <color name="car_green_600">#ff28994f</color>
+ <color name="car_green_500">#ff41af6a</color>
+ <color name="car_green_400">#ff5dba80</color>
+ <color name="car_green_300">#ff8dcfa5</color>
+ <color name="car_green_200">#ffb3dfc3</color>
+ <color name="car_green_100">#ffd5ebdf</color>
+ <color name="car_green_50">#ffe8f3ee</color>
+
+ <color name="car_red_900">#ffa81314</color>
+ <color name="car_red_800">#ffb41b1a</color>
+ <color name="car_red_700">#ffc22a2a</color>
+ <color name="car_red_600">#ffd33b30</color>
+ <color name="car_red_500">#ffe25142</color>
+ <color name="car_red_400">#ffe66a5e</color>
+ <color name="car_red_300">#ffed968d</color>
+ <color name="car_red_200">#fff3b9b3</color>
+ <color name="car_red_100">#fff7d8d9</color>
+ <color name="car_red_50">#fffaebeb</color>
+
+ <color name="car_yellow_900">#ffdd860e</color>
+ <color name="car_yellow_800">#ffe59810</color>
+ <color name="car_yellow_700">#ffeda912</color>
+ <color name="car_yellow_600">#fff3b713</color>
+ <color name="car_yellow_500">#fff5c518</color>
+ <color name="car_yellow_400">#fff6cd3a</color>
+ <color name="car_yellow_300">#fff9dc74</color>
+ <color name="car_yellow_200">#fffbe7a2</color>
+ <color name="car_yellow_100">#fffcf0ce</color>
+ <color name="car_yellow_50">#fffdf7e6</color>
+
+ <color name="car_orange_900">#ffb06000</color>
+ <color name="car_orange_800">#ffc26401</color>
+ <color name="car_orange_700">#ffd56e0c</color>
+ <color name="car_orange_600">#ffe8710a</color>
+ <color name="car_orange_500">#fffa7b17</color>
+ <color name="car_orange_400">#fffa903e</color>
+ <color name="car_orange_300">#fffcad70</color>
+ <color name="car_orange_200">#fffdc69c</color>
+ <color name="car_orange_100">#fffedfc8</color>
+ <color name="car_orange_50">#fffeefe3</color>
+
+ <color name="car_pink_900">#ff9c166b</color>
+ <color name="car_pink_800">#ffb80672</color>
+ <color name="car_pink_700">#ffd01884</color>
+ <color name="car_pink_600">#ffe52592</color>
+ <color name="car_pink_500">#fff439a0</color>
+ <color name="car_pink_400">#ffff63b8</color>
+ <color name="car_pink_300">#ffff8bcb</color>
+ <color name="car_pink_200">#fffba9d6</color>
+ <color name="car_pink_100">#fffdcfe8</color>
+ <color name="car_pink_50">#fffde7f3</color>
+
+ <color name="car_teal_900">#ff004d40</color>
+ <color name="car_teal_800">#ff00695c</color>
+ <color name="car_teal_700">#ff00796b</color>
+ <color name="car_teal_600">#ff00897b</color>
+ <color name="car_teal_500">#ff009688</color>
+ <color name="car_teal_400">#ff26a69a</color>
+ <color name="car_teal_300">#ff4db6ac</color>
+ <color name="car_teal_200">#ff80cbc4</color>
+ <color name="car_teal_100">#ffb2dfdb</color>
+ <color name="car_teal_50">#ffe0f2f1</color>
+
+ <color name="car_purple_900">#ff681da8</color>
+ <color name="car_purple_800">#ff7627bb</color>
+ <color name="car_purple_700">#ff8430ce</color>
+ <color name="car_purple_600">#ff9334e6</color>
+ <color name="car_purple_500">#ffa142f4</color>
+ <color name="car_purple_400">#ffaf5cf7</color>
+ <color name="car_purple_300">#ffc58af9</color>
+ <color name="car_purple_200">#ffd7aefb</color>
+ <color name="car_purple_100">#ffe9d2fd</color>
+ <color name="car_purple_50">#fff3e8fd</color>
+
+ <color name="car_cyan_900">#ff01877e</color>
+ <color name="car_cyan_800">#ff099091</color>
+ <color name="car_cyan_700">#ff12a4af</color>
+ <color name="car_cyan_600">#ff12b5cb</color>
+ <color name="car_cyan_500">#ff24c1e0</color>
+ <color name="car_cyan_400">#ff4ecde6</color>
+ <color name="car_cyan_300">#ff78d9ec</color>
+ <color name="car_cyan_200">#ffa1e4f2</color>
+ <color name="car_cyan_100">#ffcbf0f8</color>
+ <color name="car_cyan_50">#ffe4f7fb</color>
+
+
+ <color name="car_grey_1000">#cc000000</color>
+ <color name="car_white_1000">#1effffff</color>
+ <color name="car_blue_grey_800">#ff37474F</color>
+ <color name="car_blue_grey_900">#ff263238</color>
+ <color name="car_dark_blue_grey_600">#ff1d272d</color>
+ <color name="car_dark_blue_grey_700">#ff172026</color>
+ <color name="car_dark_blue_grey_800">#ff11181d</color>
+ <color name="car_dark_blue_grey_900">#ff0c1013</color>
+ <color name="car_dark_blue_grey_1000">#ff090c0f</color>
+ <color name="car_light_blue_300">#ff4fc3f7</color>
+ <color name="car_light_blue_500">#ff03A9F4</color>
+ <color name="car_light_blue_600">#ff039be5</color>
+ <color name="car_light_blue_700">#ff0288d1</color>
+ <color name="car_light_blue_800">#ff0277bd</color>
+ <color name="car_light_blue_900">#ff01579b</color>
+
+
+ <color name="car_red_500a">#ffd50000</color>
+ <color name="car_red_a700">#ffd50000</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1404383..0daf5a7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2113,8 +2113,8 @@
<!-- Type of the long press sensor. Empty if long press is not supported. -->
<string name="config_dozeLongPressSensorType" translatable="false"></string>
- <!-- Type of the reach sensor. Empty if reach is not supported. -->
- <string name="config_dozeReachSensorType" translatable="false"></string>
+ <!-- Type of sensor that wakes up the lock screen. Empty if not supported. -->
+ <string name="config_dozeWakeLockScreenSensorType" translatable="false"></string>
<!-- Type of the wake up sensor. Empty if not supported. -->
<string name="config_dozeWakeScreenSensorType" translatable="false"></string>
@@ -3541,4 +3541,13 @@
<!-- Brand value for attestation of misprovisioned device. -->
<string name="config_misprovisionedBrandValue" translatable="false"></string>
+
+ <!-- Pre-scale volume at volume step 1 for Absolute Volume -->
+ <fraction name="config_prescaleAbsoluteVolume_index1">50%</fraction>
+
+ <!-- Pre-scale volume at volume step 2 for Absolute Volume -->
+ <fraction name="config_prescaleAbsoluteVolume_index2">70%</fraction>
+
+ <!-- Pre-scale volume at volume step 3 for Absolute Volume -->
+ <fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
</resources>
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index 7d14f86..a0c02ed 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -16,12 +16,86 @@
*/
-->
<resources>
- <!-- TODO replace with car support lib sizes when available -->
<dimen name="car_fullscreen_user_pod_icon_text_size">64sp</dimen>
<dimen name="car_fullscreen_user_pod_width">243dp</dimen>
<dimen name="car_fullscreen_user_pod_height">356dp</dimen>
<dimen name="car_fullscreen_user_pod_image_avatar_width">96dp</dimen>
<dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen>
- <dimen name="car_padding_4">20dp</dimen>
+
+
+ <!-- Application Bar -->
+ <dimen name="car_app_bar_height">80dp</dimen>
+ <!-- Margin -->
+ <dimen name="car_margin">20dp</dimen>
+ <!-- Lists -->
+ <dimen name="car_single_line_list_item_height">96dp</dimen>
+ <dimen name="car_double_line_list_item_height">@dimen/car_single_line_list_item_height</dimen>
+ <dimen name="car_list_divider_height">1dp</dimen>
+ <!-- The diff between keyline 1 and keyline 3. -->
+ <dimen name="car_keyline_1_keyline_3_diff">88dp</dimen>
+ <dimen name="car_dialog_action_bar_height">@dimen/car_card_action_bar_height</dimen>
+ <dimen name="car_primary_icon_size">44dp</dimen>
+
+ <!-- Text size for car -->
+ <dimen name="car_title_size">32sp</dimen>
+ <dimen name="car_title2_size">32sp</dimen>
+ <dimen name="car_headline1_size">45sp</dimen>
+ <dimen name="car_headline2_size">32sp</dimen>
+ <dimen name="car_headline3_size">24sp</dimen>
+ <dimen name="car_headline4_size">20sp</dimen>
<dimen name="car_body1_size">32sp</dimen>
-</resources>
\ No newline at end of file
+ <dimen name="car_body2_size">26sp</dimen>
+ <dimen name="car_body3_size">16sp</dimen>
+ <dimen name="car_body4_size">14sp</dimen>
+ <dimen name="car_body5_size">18sp</dimen>
+ <dimen name="car_label1_size">26sp</dimen>
+ <dimen name="car_label2_size">64sp</dimen>
+ <dimen name="car_action1_size">26sp</dimen>
+ <dimen name="car_action2_size">26sp</dimen>
+
+ <!-- Common icon size for car app -->
+ <dimen name="car_icon_size">56dp</dimen>
+
+ <dimen name="car_card_header_height">96dp</dimen>
+ <dimen name="car_card_action_bar_height">96dp</dimen>
+
+ <!-- Paddings -->
+ <dimen name="car_padding_1">4dp</dimen>
+ <dimen name="car_padding_2">10dp</dimen>
+ <dimen name="car_padding_3">16dp</dimen>
+ <dimen name="car_padding_4">28dp</dimen>
+ <dimen name="car_padding_5">32dp</dimen>
+
+ <!-- Radius -->
+ <dimen name="car_radius_1">4dp</dimen>
+ <dimen name="car_radius_2">8dp</dimen>
+ <dimen name="car_radius_3">16dp</dimen>
+ <dimen name="car_radius_5">100dp</dimen>
+
+ <!-- Keylines for content. -->
+ <dimen name="car_keyline_1">48dp</dimen>
+ <dimen name="car_keyline_2">108dp</dimen>
+ <dimen name="car_keyline_3">152dp</dimen>
+ <dimen name="car_keyline_4">182dp</dimen>
+
+ <!-- Buttons -->
+ <dimen name="car_button_height">56dp</dimen>
+ <dimen name="car_button_min_width">158dp</dimen>
+ <dimen name="car_button_horizontal_padding">@dimen/car_padding_4</dimen>
+ <dimen name="car_borderless_button_horizontal_padding">0dp</dimen>
+ <dimen name="car_button_radius">@dimen/car_radius_1</dimen>
+ <dimen name="car_pill_button_size">56dp</dimen>
+
+ <!-- Seekbar -->
+ <dimen name="car_seekbar_height">6dp</dimen>
+ <dimen name="car_seekbar_padding">26dp</dimen>
+ <dimen name="car_seekbar_thumb_size">24dp</dimen>
+ <dimen name="car_seekbar_thumb_stroke">1dp</dimen>
+ <!-- The space between seekbar and text in ListItem. This value is based on car_seekbar_padding.
+ It brings seekbar and text closer for visual balance while maintaining touch area. -->
+ <dimen name="car_seekbar_text_overlap">-20dp</dimen>
+
+ <!-- Progress Bar -->
+ <dimen name="car_progress_bar_height">@dimen/car_seekbar_height</dimen>
+
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cc99a4e..fadefff 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2912,6 +2912,8 @@
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="usesNonSdkApi" />
<public name="minimumUiTimeout" />
+ <public name="isLightTheme" />
+ <public name="isSplitRequired" />
</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 64620f3..6d0127a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1439,6 +1439,10 @@
<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] -->
+ <string name="face_authenticated_no_confirmation_required">Face authenticated</string>
+ <!-- Accessibility message announced when a face has been authenticated, but requires the user to press the confirm button [CHAR LIMIT=NONE] -->
+ <string name="face_authenticated_confirmation_required">Face authenticated, please press confirm</string>
<!-- Error message shown when the fingerprint hardware can't be accessed -->
<string name="fingerprint_error_hw_not_available">Fingerprint hardware not available.</string>
@@ -1516,6 +1520,8 @@
<string name="face_error_no_space">Face can\u2019t be stored.</string>
<!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
<string name="face_error_canceled">Face operation canceled.</string>
+ <!-- Generic error message shown when the face authentication operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=50] -->
+ <string name="face_error_user_canceled">Face authentication canceled by user.</string>
<!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
<string name="face_error_lockout">Too many attempts. Try again later.</string>
<!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml
new file mode 100644
index 0000000..f6ff1b6
--- /dev/null
+++ b/core/res/res/values/styles_car.xml
@@ -0,0 +1,89 @@
+<?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>
+ <!-- Car text -->
+ <style name="CarBody1">
+ <item name="textStyle">normal</item>
+ <item name="textSize">@dimen/car_body1_size</item>
+ <item name="textColor">@color/car_body1</item>
+ </style>
+
+ <style name="CarBody1.Light">
+ <item name="textColor">@color/car_body1_light</item>
+ </style>
+
+ <style name="CarBody1.Dark">
+ <item name="textColor">@color/car_body2_dark</item>
+ </style>
+
+ <style name="CarBody2">
+ <item name="textStyle">normal</item>
+ <item name="textSize">@dimen/car_body2_size</item>
+ <item name="textColor">@color/car_body2</item>
+ </style>
+
+ <style name="CarBody2.Dark">
+ <item name="textColor">@color/car_body2_dark</item>
+ </style>
+ <style name="CarBody2.Light">
+ <item name="textColor">@color/car_body2_light</item>
+ </style>
+
+ <style name="CarBody3">
+ <item name="textStyle">normal</item>
+ <item name="textSize">@dimen/car_body3_size</item>
+ <item name="textColor">@color/car_body3</item>
+ </style>
+
+ <!-- The smallest styling for body text. The color of this text changes based on the day/night
+ mode. -->
+ <style name="CarBody4">
+ <item name="textStyle">normal</item>
+ <item name="textSize">@dimen/car_body4_size</item>
+ <item name="textColor">@color/car_body4</item>
+ </style>
+
+ <style name="CarAction1">
+ <item name="textStyle">bold</item>
+ <item name="textSize">@dimen/car_action1_size</item>
+ <item name="textColor">@color/car_highlight</item>
+ </style>
+
+ <style name="CarAction1.Dark">
+ <item name="textColor">@color/car_highlight_dark</item>
+ </style>
+ <style name="CarAction1.Light">
+ <item name="textColor">@color/car_highlight_light</item>
+ </style>
+
+ <!-- The styling for title text. The color of this text changes based on day/night mode. -->
+ <style name="CarTitle" >
+ <item name="textStyle">bold</item>
+ <item name="textSize">@dimen/car_title2_size</item>
+ <item name="textColor">@color/car_title</item>
+ </style>
+
+ <!-- Title text that is permanently a dark color. -->
+ <style name="CarTitle.Dark" >
+ <item name="textColor">@color/car_title_dark</item>
+ </style>
+
+ <!-- Title text that is permanently a light color. -->
+ <style name="CarTitle.Light" >
+ <item name="textColor">@color/car_title_light</item>
+ </style>
+
+</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7b8eced..72ae0d6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2429,6 +2429,7 @@
<java-symbol type="string" name="face_error_timeout" />
<java-symbol type="array" name="face_error_vendor" />
<java-symbol type="string" name="face_error_canceled" />
+ <java-symbol type="string" name="face_error_user_canceled" />
<java-symbol type="string" name="face_error_lockout" />
<java-symbol type="string" name="face_error_lockout_permanent" />
<java-symbol type="string" name="face_error_not_enrolled" />
@@ -2446,6 +2447,8 @@
<java-symbol type="string" name="face_acquired_not_detected" />
<java-symbol type="array" name="face_acquired_vendor" />
<java-symbol type="string" name="face_name_template" />
+ <java-symbol type="string" name="face_authenticated_no_confirmation_required" />
+ <java-symbol type="string" name="face_authenticated_confirmation_required" />
<!-- Face config -->
<java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
@@ -3151,7 +3154,9 @@
<java-symbol type="integer" name="autofill_max_visible_datasets" />
<java-symbol type="style" name="Theme.DeviceDefault.Autofill" />
+ <java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill" />
<java-symbol type="style" name="Theme.DeviceDefault.Autofill.Save" />
+ <java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill.Save" />
<java-symbol type="dimen" name="notification_big_picture_max_height"/>
<java-symbol type="dimen" name="notification_big_picture_max_width"/>
@@ -3274,7 +3279,7 @@
<java-symbol type="array" name="config_hideWhenDisabled_packageNames" />
<java-symbol type="string" name="config_dozeLongPressSensorType" />
- <java-symbol type="string" name="config_dozeReachSensorType" />
+ <java-symbol type="string" name="config_dozeWakeLockScreenSensorType" />
<java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
@@ -3465,4 +3470,9 @@
<java-symbol type="integer" name="db_wal_truncate_size" />
<java-symbol type="integer" name="config_wakeUpDelayDoze" />
<java-symbol type="string" name="config_dozeWakeScreenSensorType" />
+
+ <!-- For Bluetooth AbsoluteVolume -->
+ <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
+ <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" />
+ <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 92096ab..3385527 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1681,13 +1681,14 @@
</style>
- <!-- @hide DeviceDefault theme for the autofill FillUi -->
- <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill.Light">
- </style>
+ <!-- @hide DeviceDefault themes for the autofill FillUi -->
+ <style name="Theme.DeviceDefault.Autofill" />
+ <style name="Theme.DeviceDefault.Light.Autofill" />
- <!-- @hide DeviceDefault theme for the autofill SaveUi -->
- <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save.Light">
- </style>
+ <!-- @hide DeviceDefault theme for the autofill SaveUi. NOTE: it must be a .Panel so the dialog
+ is shown at the bottom of the screen -->
+ <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.DeviceDefault.Panel"/>
+ <style name="Theme.DeviceDefault.Light.Autofill.Save" parent="Theme.DeviceDefault.Light.Panel"/>
<!-- DeviceDefault theme for the default system theme. -->
<style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" />
@@ -1715,4 +1716,10 @@
<item name="colorAccent">@color/accent_device_default_dark</item>
</style>
+ <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_device_default_light</item>
+ <item name="layout_gravity">center</item>
+ </style>
+
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index b3e33d5..ccaf041 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1418,24 +1418,4 @@
<item name="colorSecondary">@color/secondary_material_settings</item>
</style>
- <!-- @hide -->
- <style name="Theme.Material.Autofill" parent="Theme.Material">
- <item name="colorBackground">@color/autofill_background_material_dark</item>
- </style>
-
- <!-- @hide -->
- <style name="Theme.Material.Autofill.Light" parent="Theme.Material.Light">
- <item name="colorBackground">@color/autofill_background_material_light</item>
- </style>
-
- <!-- @hide -->
- <style name="Theme.Material.Autofill.Save" parent="Theme.Material.Panel">
- <item name="colorBackground">@color/autofill_background_material_dark</item>
- </style>
-
- <!-- @hide -->
- <style name="Theme.Material.Autofill.Save.Light" parent="Theme.Material.Light.Panel">
- <item name="colorBackground">@color/autofill_background_material_light</item>
- </style>
-
</resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index cb4e5c4..6cf6a82 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,13 +18,17 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="7">
+<zen version="8">
<allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
reminders="false" events="false" repeatCallers="true" />
-
+ <automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
+ component="android/com.android.server.notification.EventConditionProvider"
+ conditionId="condition://android/event?userId=-10000&calendar=&reply=1"/>
+ <automatic ruleId="EVERY_NIGHT_DEFAULT_RULE" enabled="false" snoozing="false" name="Sleeping"
+ zen="1" component="android/com.android.server.notification.ScheduleConditionProvider"
+ conditionId="condition://android/schedule?days=1.2.3.4.5.6.7&start=22.0&end=7.0&exitAtAlarm=true"/>
<!-- all visual effects that exist as of P -->
<disallow visualEffects="511" />
-
<!-- whether there are notification channels that can bypass dnd -->
<state areChannelsBypassingDnd="false" />
</zen>
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
new file mode 100644
index 0000000..2142f27
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.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.content;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.withSettings;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentProviderTest {
+
+ private Context mContext;
+ private ContentProvider mCp;
+
+ private ApplicationInfo mProviderApp;
+ private ProviderInfo mProvider;
+
+ @Before
+ public void setUp() {
+ mProviderApp = new ApplicationInfo();
+ mProviderApp.uid = 10001;
+
+ mProvider = new ProviderInfo();
+ mProvider.authority = "com.example";
+ mProvider.applicationInfo = mProviderApp;
+
+ mContext = mock(Context.class);
+
+ mCp = mock(ContentProvider.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));
+ mCp.attachInfo(mContext, mProvider);
+ }
+
+ @Test
+ public void testValidateIncomingUri_Normal() throws Exception {
+ assertEquals(Uri.parse("content://com.example/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/")));
+ assertEquals(Uri.parse("content://com.example/foo/bar"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo/bar")));
+ assertEquals(Uri.parse("content://com.example/foo%2Fbar"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2Fbar")));
+ assertEquals(Uri.parse("content://com.example/foo%2F%2Fbar"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2F%2Fbar")));
+ }
+
+ @Test
+ public void testValidateIncomingUri_Shady() throws Exception {
+ assertEquals(Uri.parse("content://com.example/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example//")));
+ assertEquals(Uri.parse("content://com.example/foo/bar/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example//foo//bar//")));
+ assertEquals(Uri.parse("content://com.example/foo/bar/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo///bar/")));
+ assertEquals(Uri.parse("content://com.example/foo%2F%2Fbar/baz"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2F%2Fbar//baz")));
+ }
+
+ @Test
+ public void testValidateIncomingUri_NonPath() throws Exception {
+ // We only touch paths; queries and fragments are left intact
+ assertEquals(Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar"),
+ mCp.validateIncomingUri(
+ Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar")));
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 3ce2589..6fdb71f 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.fonts.Font;
+import android.graphics.fonts.FontCustomizationParser;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.support.test.InstrumentationRegistry;
@@ -36,12 +37,15 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
@@ -62,6 +66,8 @@
};
private static final String TEST_FONTS_XML;
private static final String TEST_FONT_DIR;
+ private static final String TEST_OEM_XML;
+ private static final String TEST_OEM_DIR;
private static final float GLYPH_1EM_WIDTH;
private static final float GLYPH_2EM_WIDTH;
@@ -73,8 +79,13 @@
if (!cacheDir.isDirectory()) {
cacheDir.mkdirs();
}
- TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/";
+ TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/fonts/";
TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath();
+ TEST_OEM_DIR = cacheDir.getAbsolutePath() + "/oem_fonts/";
+ TEST_OEM_XML = new File(cacheDir, "fonts_customization.xml").getAbsolutePath();
+
+ new File(TEST_FONT_DIR).mkdirs();
+ new File(TEST_OEM_DIR).mkdirs();
final AssetManager am =
InstrumentationRegistry.getInstrumentation().getContext().getAssets();
@@ -99,6 +110,12 @@
} catch (IOException e) {
throw new RuntimeException(e);
}
+ final File outOemInCache = new File(TEST_OEM_DIR, fontFile);
+ try (InputStream is = am.open(sourceInAsset)) {
+ Files.copy(is, outOemInCache.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
}
@@ -107,11 +124,14 @@
for (final String fontFile : TEST_FONT_FILES) {
final File outInCache = new File(TEST_FONT_DIR, fontFile);
outInCache.delete();
+ final File outOemInCache = new File(TEST_OEM_DIR, fontFile);
+ outInCache.delete();
}
}
private static void buildSystemFallback(String xml,
- ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
+ FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap,
+ ArrayMap<String, FontFamily[]> fallbackMap) {
final ArrayList<Font> availableFonts = new ArrayList<>();
try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
fos.write(xml.getBytes(Charset.forName("UTF-8")));
@@ -119,18 +139,28 @@
throw new RuntimeException(e);
}
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML,
- TEST_FONT_DIR, fallbackMap, availableFonts);
+ TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases);
}
+ private static FontCustomizationParser.Result readFontCustomization(String oemXml) {
+ try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) {
+ return FontCustomizationParser.parse(is, TEST_OEM_DIR);
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Test
public void testBuildSystemFallback() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
final ArrayList<Font> availableFonts = new ArrayList<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
- SYSTEM_FONT_DIR, fallbackMap, availableFonts);
+ SYSTEM_FONT_DIR, oemCustomization, fallbackMap, availableFonts);
assertNotNull(aliases);
assertFalse(fallbackMap.isEmpty());
@@ -156,8 +186,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
assertEquals(1, fontMap.size());
assertTrue(fontMap.containsKey("sans-serif"));
@@ -184,8 +216,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -230,8 +264,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -275,8 +311,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -325,8 +363,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -371,8 +411,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -410,8 +452,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -449,8 +493,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -497,8 +543,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
paint.setTypeface(fontMap.get("sans-serif"));
@@ -539,8 +587,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -578,8 +628,10 @@
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
- buildSystemFallback(xml, fontMap, fallbackMap);
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
final Paint paint = new Paint();
@@ -598,4 +650,191 @@
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
+ @Test
+ public void testBuildSystemFallback__Customization_new_named_family() {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font weight='400' style='normal'>a3em.ttf</font>"
+ + " </family>"
+ + "</familyset>";
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-named-family' name='google-sans'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + "</fonts-modification>";
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ readFontCustomization(oemXml);
+
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+ final Paint paint = new Paint();
+
+ Typeface testTypeface = fontMap.get("sans-serif");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+ testTypeface = fontMap.get("google-sans");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
+
+ @Test
+ public void testBuildSystemFallback__Customization_new_named_family_override() {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font weight='400' style='normal'>a3em.ttf</font>"
+ + " </family>"
+ + "</familyset>";
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-named-family' name='sans-serif'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + "</fonts-modification>";
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ readFontCustomization(oemXml);
+
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+ final Paint paint = new Paint();
+
+ Typeface testTypeface = fontMap.get("sans-serif");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
+
+ @Test
+ public void testBuildSystemFallback__Customization_additional_alias() {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font weight='400' style='normal'>a3em.ttf</font>"
+ + " </family>"
+ + "</familyset>";
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-named-family' name='google-sans'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " <font weight='700' style='normal'>c3em.ttf</font>"
+ + " </family>"
+ + " <alias name='another-google-sans' to='google-sans' />"
+ + " <alias name='google-sans-bold' to='google-sans' weight='700' />"
+ + "</fonts-modification>";
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ readFontCustomization(oemXml);
+
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+ final Paint paint = new Paint();
+
+ Typeface testTypeface = fontMap.get("sans-serif");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+ testTypeface = fontMap.get("google-sans");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+ testTypeface = fontMap.get("another-google-sans");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+ testTypeface = fontMap.get("google-sans-bold");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
+
+ @Test
+ public void testBuildSystemFallback__Customization_additional_alias_conflict_with_new_name() {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='named-family'>"
+ + " <font weight='400' style='normal'>a3em.ttf</font>"
+ + " </family>"
+ + " <alias name='named-alias' to='named-family' />"
+ + "</familyset>";
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-named-family' name='named-alias'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + "</fonts-modification>";
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final FontCustomizationParser.Result oemCustomization =
+ readFontCustomization(oemXml);
+
+ buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+ final Paint paint = new Paint();
+
+ Typeface testTypeface = fontMap.get("named-family");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+ testTypeface = fontMap.get("named-alias");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() {
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-named-family'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + "</fonts-modification>";
+ readFontCustomization(oemXml);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildSystemFallback__Customization_new_named_family_dup_name_exception() {
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-named-family' name='google-sans'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + " <family customizationType='new-named-family' name='google-sans'>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + "</fonts-modification>";
+ readFontCustomization(oemXml);
+ }
}
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
index 1349844..1286b13 100644
--- a/core/tests/coretests/src/android/net/LocalSocketTest.java
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -22,6 +22,7 @@
import android.net.LocalSocketAddress;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
+
import junit.framework.TestCase;
import java.io.FileDescriptor;
@@ -39,6 +40,20 @@
ls = new LocalSocket();
+ try {
+ ls.connect(new LocalSocketAddress(null));
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // pass
+ }
+
+ try {
+ ls.bind(new LocalSocketAddress(null));
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // pass
+ }
+
ls.connect(new LocalSocketAddress("android.net.LocalSocketTest"));
ls1 = ss.accept();
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index fee470d..d2420ee 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -114,6 +114,7 @@
Settings.Global.ANOMALY_CONFIG_VERSION,
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
Settings.Global.APN_DB_UPDATE_METADATA_URL,
+ Settings.Global.APP_BINDING_CONSTANTS,
Settings.Global.APP_IDLE_CONSTANTS,
Settings.Global.APP_OPS_CONSTANTS,
Settings.Global.APP_STANDBY_ENABLED,
@@ -447,6 +448,7 @@
Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
Settings.Global.GPU_DEBUG_APP,
Settings.Global.GPU_DEBUG_LAYERS,
+ Settings.Global.ANGLE_ENABLED_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index 8909293..b0d29bd 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -73,7 +73,7 @@
@Test
public void testComponentNameValidator() {
assertTrue(SettingsValidators.COMPONENT_NAME_VALIDATOR.validate(
- "android/com.android.internal.backup.LocalTransport"));
+ "com.android.localtransport/.LocalTransport"));
assertFalse(SettingsValidators.COMPONENT_NAME_VALIDATOR.validate("rectangle"));
}
@@ -90,7 +90,7 @@
@Test
public void testNullableComponentNameValidator_onValidComponentName_returnsTrue() {
assertTrue(SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR.validate(
- "android/com.android.internal.backup.LocalTransport"));
+ "com.android.localtransport/.LocalTransport"));
}
@Test
@@ -185,7 +185,7 @@
@Test
public void testComponentNameListValidator() {
Validator v = new SettingsValidators.ComponentNameListValidator(",");
- assertTrue(v.validate("android/com.android.internal.backup.LocalTransport,"
+ assertTrue(v.validate("com.android.localtransport/.LocalTransport,"
+ "com.google.android.gms/.backup.migrate.service.D2dTransport"));
assertFalse(v.validate("com.google.5android,android"));
}
@@ -200,7 +200,7 @@
@Test
public void testPackageNameListValidator() {
Validator v = new SettingsValidators.PackageNameListValidator(",");
- assertTrue(v.validate("com.android.internal.backup.LocalTransport,com.google.android.gms"));
+ assertTrue(v.validate("com.android.localtransport.LocalTransport,com.google.android.gms"));
assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android"));
}
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 898e78c..5592aac 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -21,6 +21,7 @@
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
+import android.graphics.fonts.FontCustomizationParser;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.support.test.InstrumentationRegistry;
@@ -77,8 +78,10 @@
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
final ArrayList<Font> availableFonts = new ArrayList<>();
+ final FontCustomizationParser.Result oemCustomization =
+ new FontCustomizationParser.Result();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml,
- mTestFontsDir, fallbackMap, availableFonts);
+ mTestFontsDir, oemCustomization, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases);
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
new file mode 100644
index 0000000..55e5e36
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.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 android.view.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodManagerTest {
+ @Test
+ public void testPrivateApiGetInstance() throws Exception {
+ final InputMethodManager globalImm = InputMethodManager.getInstance();
+ assertNotNull("InputMethodManager.getInstance() still needs to work due to"
+ + " @UnsupportedAppUsage", globalImm);
+ assertEquals("InputMethodManager.peekInstance() still needs to work due to"
+ + " @UnsupportedAppUsage", globalImm, InputMethodManager.peekInstance());
+
+ final Context testContext = InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+
+ final WindowManager wm = testContext.getSystemService(WindowManager.class);
+ final Context defaultDisplayContext =
+ testContext.createDisplayContext(wm.getDefaultDisplay());
+ final InputMethodManager imm =
+ defaultDisplayContext.getSystemService(InputMethodManager.class);
+ assertEquals("InputMethodManager.getInstance() always returns the instance for the default"
+ + " display.", globalImm, imm);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
index efdd7e9..8360126 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
@@ -43,7 +43,7 @@
/**
* Test class for {@link KernelCpuProcReader}.
*
- * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReader
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReaderTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
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 565a3ec..0c8dd9d 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -21,6 +21,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.Message;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -74,14 +75,17 @@
public void testSingleMessageDispatched() {
TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ Message message = mHandlerFirst.obtainMessage(1000);
+ message.workSourceUid = 1000;
Object token = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
looperStats.tickThreadTime(10);
- looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000));
+ looperStats.messageDispatched(token, message);
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
assertThat(entries).hasSize(1);
LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.workSourceUid).isEqualTo(1000);
assertThat(entry.threadName).isEqualTo("TestThread1");
assertThat(entry.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -100,15 +104,17 @@
public void testThrewException() {
TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ Message message = mHandlerFirst.obtainMessage(7);
+ message.workSourceUid = 123;
Object token = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
looperStats.tickThreadTime(10);
- looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7),
- new ArithmeticException());
+ looperStats.dispatchingThrewException(token, message, new ArithmeticException());
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
assertThat(entries).hasSize(1);
LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.workSourceUid).isEqualTo(123);
assertThat(entry.threadName).isEqualTo("TestThread1");
assertThat(entry.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -146,31 +152,39 @@
looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
}));
- // Contributes to entry1.
+ // Will not be sampled so does not contribute to any entries.
Object token4 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(0));
+
+ // Contributes to entry1.
+ Object token5 = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
looperStats.tickThreadTime(100);
- looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1));
+ looperStats.messageDispatched(token5, mHandlerAnonymous.obtainMessage(1));
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
assertThat(entries).hasSize(3);
entries.sort(Comparator.comparing(e -> e.handlerClassName));
- // Captures data for token4 call.
+ // Captures data for token5 call.
LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.workSourceUid).isEqualTo(-1);
assertThat(entry1.threadName).isEqualTo("TestThread1");
assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
assertThat(entry1.messageCount).isEqualTo(1);
- assertThat(entry1.recordedMessageCount).isEqualTo(0);
+ assertThat(entry1.recordedMessageCount).isEqualTo(1);
assertThat(entry1.exceptionCount).isEqualTo(0);
- assertThat(entry1.totalLatencyMicros).isEqualTo(0);
- assertThat(entry1.maxLatencyMicros).isEqualTo(0);
- assertThat(entry1.cpuUsageMicros).isEqualTo(0);
- assertThat(entry1.maxCpuUsageMicros).isEqualTo(0);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(100);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(100);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(100);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(100);
// Captures data for token1 and token2 calls.
LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.workSourceUid).isEqualTo(-1);
assertThat(entry2.threadName).isEqualTo("TestThread1");
assertThat(entry2.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -185,6 +199,7 @@
// Captures data for token3 call.
LooperStats.ExportedEntry entry3 = entries.get(2);
+ assertThat(entry3.workSourceUid).isEqualTo(-1);
assertThat(entry3.threadName).isEqualTo("TestThread2");
assertThat(entry3.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerSecond");
@@ -265,7 +280,7 @@
@Test
public void testMessagesOverSizeCap() {
- TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */);
+ TestableLooperStats looperStats = new TestableLooperStats(1, 1 /* sizeCap */);
Object token1 = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
@@ -296,12 +311,12 @@
assertThat(entry1.handlerClassName).isEqualTo("");
assertThat(entry1.messageName).isEqualTo("OVERFLOW");
assertThat(entry1.messageCount).isEqualTo(3);
- assertThat(entry1.recordedMessageCount).isEqualTo(1);
+ assertThat(entry1.recordedMessageCount).isEqualTo(3);
assertThat(entry1.exceptionCount).isEqualTo(0);
- assertThat(entry1.totalLatencyMicros).isEqualTo(10);
- assertThat(entry1.maxLatencyMicros).isEqualTo(10);
- assertThat(entry1.cpuUsageMicros).isEqualTo(10);
- assertThat(entry1.maxCpuUsageMicros).isEqualTo(10);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(70);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(50);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(40);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(20);
LooperStats.ExportedEntry entry2 = entries.get(1);
assertThat(entry2.threadName).isEqualTo("TestThread1");
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
new file mode 100644
index 0000000..c051a1c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+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 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.nio.file.Files;
+
+
+/**
+ * Test class for {@link StoragedUidIoStatsReader}.
+ *
+ * To run it:
+ * atest FrameworksCoreTests:com.android.internal.os.StoragedUidIoStatsReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StoragedUidIoStatsReaderTest {
+
+ private File mRoot;
+ private File mTestDir;
+ private File mTestFile;
+ // private Random mRand = new Random();
+
+ private StoragedUidIoStatsReader mStoragedUidIoStatsReader;
+ @Mock
+ private StoragedUidIoStatsReader.Callback mCallback;
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mRoot = getContext().getFilesDir();
+ mTestFile = new File(mTestDir, "test.file");
+ mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(mTestFile.getAbsolutePath());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(mRoot);
+ }
+
+
+ /**
+ * Tests that reading will never call the callback.
+ */
+ @Test
+ public void testReadNonexistentFile() throws Exception {
+ mStoragedUidIoStatsReader.readAbsolute(mCallback);
+ verifyZeroInteractions(mCallback);
+
+ }
+
+ /**
+ * Tests that reading a file with 3 uids works as expected.
+ */
+ @Test
+ public void testReadExpected() throws Exception {
+ BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
+ int[] uids = {0, 100, 200};
+ long[] fg_chars_read = {1L, 101L, 201L};
+ long[] fg_chars_write = {2L, 102L, 202L};
+ long[] fg_bytes_read = {3L, 103L, 203L};
+ long[] fg_bytes_write = {4L, 104L, 204L};
+ long[] bg_chars_read = {5L, 105L, 205L};
+ long[] bg_chars_write = {6L, 106L, 206L};
+ long[] bg_bytes_read = {7L, 107L, 207L};
+ long[] bg_bytes_write = {8L, 108L, 208L};
+ long[] fg_fsync = {9L, 109L, 209L};
+ long[] bg_fsync = {10L, 110L, 210L};
+
+ for (int i = 0; i < uids.length; i++) {
+ bufferedWriter.write(String
+ .format("%d %d %d %d %d %d %d %d %d %d %d\n", uids[i], fg_chars_read[i],
+ fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i],
+ bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i],
+ bg_bytes_write[i], fg_fsync[i], bg_fsync[i]));
+ }
+ bufferedWriter.close();
+
+ mStoragedUidIoStatsReader.readAbsolute(mCallback);
+ for (int i = 0; i < uids.length; i++) {
+ verify(mCallback).onUidStorageStats(uids[i], fg_chars_read[i], fg_chars_write[i],
+ fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i],
+ bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i]);
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ }
+
+ /**
+ * Tests that a line with less than 11 items is passed over.
+ */
+ @Test
+ public void testLineDoesNotElevenEntries() throws Exception {
+ BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
+
+ // Only has 10 numbers.
+ bufferedWriter.write(String
+ .format("%d %d %d %d %d %d %d %d %d %d\n", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
+
+ bufferedWriter.write(String
+ .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20));
+ bufferedWriter.close();
+
+ // Make sure we get the second line, but the first is skipped.
+ mStoragedUidIoStatsReader.readAbsolute(mCallback);
+ verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
+ verifyNoMoreInteractions(mCallback);
+ }
+
+
+ /**
+ * Tests that a line that is malformed is passed over.
+ */
+ @Test
+ public void testLineIsMalformed() throws Exception {
+ BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
+
+ // Line is not formatted properly. It has a string.
+ bufferedWriter.write(String
+ .format("%d %d %d %d %d %s %d %d %d %d %d\n", 0, 1, 2, 3, 4, "NotANumber", 5, 6, 7,
+ 8, 9));
+
+ bufferedWriter.write(String
+ .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20));
+ bufferedWriter.close();
+
+ // Make sure we get the second line, but the first is skipped.
+ mStoragedUidIoStatsReader.readAbsolute(mCallback);
+ verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
+ verifyNoMoreInteractions(mCallback);
+ }
+}
diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml
index ae6a7f6..b0d2de1 100644
--- a/data/etc/framework-sysconfig.xml
+++ b/data/etc/framework-sysconfig.xml
@@ -28,7 +28,7 @@
<!-- Whitelist of what components are permitted as backup data transports. The
'service' attribute here is a flattened ComponentName string. -->
<backup-transport-whitelisted-service
- service="android/com.android.internal.backup.LocalTransportService" />
+ service="com.android.localtransport/.LocalTransportService" />
<!-- Whitelist of bundled applications which all handle URLs to their websites by default -->
<app-link package="com.android.carrierdefaultapp" />
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 76eb4e6..454dceb 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -89,23 +89,7 @@
LOCAL_MODULE := fonts.xml
LOCAL_MODULE_CLASS := ETC
-
-AOSP_FONTS_FILE := frameworks/base/data/fonts/fonts.xml
-
-ifdef ADDITIONAL_FONTS_FILE
-ADDITIONAL_FONTS_SCRIPT := frameworks/base/tools/fonts/add_additional_fonts.py
-ADD_ADDITIONAL_FONTS := $(local-generated-sources-dir)/fonts.xml
-
-$(ADD_ADDITIONAL_FONTS): PRIVATE_SCRIPT := $(ADDITIONAL_FONTS_SCRIPT)
-$(ADD_ADDITIONAL_FONTS): PRIVATE_ADDITIONAL_FONTS_FILE := $(ADDITIONAL_FONTS_FILE)
-$(ADD_ADDITIONAL_FONTS): $(ADDITIONAL_FONTS_SCRIPT) $(AOSP_FONTS_FILE) $(ADDITIONAL_FONTS_FILE)
- rm -f $@
- python $(PRIVATE_SCRIPT) $@ $(PRIVATE_ADDITIONAL_FONTS_FILE)
-else
-ADD_ADDITIONAL_FONTS := $(AOSP_FONTS_FILE)
-endif
-
-LOCAL_PREBUILT_MODULE_FILE := $(ADD_ADDITIONAL_FONTS)
+LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml
include $(BUILD_PREBUILT)
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 522db3c..b463dd8 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -21,8 +21,6 @@
key 0x126 DPAD_DOWN
key 0x127 DPAD_LEFT
-key 0x120 BUTTON_SELECT
-key 0x123 BUTTON_START
key 0x12e BUTTON_A
key 0x12d BUTTON_B
key 0x12f BUTTON_X
@@ -34,9 +32,6 @@
key 0x121 BUTTON_THUMBL
key 0x122 BUTTON_THUMBR
-# PS key
-key 0x2d0 BUTTON_MODE
-
# Left Analog Stick
axis 0x00 X
axis 0x01 Y
@@ -74,3 +69,11 @@
# Square
# axis 0x37
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x120 BUTTON_SELECT
+# Start
+key 0x123 BUTTON_START
+# PS key
+key 0x2d0 BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
@@ -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.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+# and when connected over Bluetooth
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
@@ -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.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+# and when connected over Bluetooth
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
new file mode 100644
index 0000000..5fe35f7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
@@ -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.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when
+# connected over USB
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -60,7 +60,6 @@
key 0x138 BUTTON_SELECT
# Options
key 0x139 BUTTON_START
-
# PS key
key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+# connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -60,7 +60,6 @@
key 0x138 BUTTON_SELECT
# Options
key 0x139 BUTTON_START
-
# PS key
key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+# connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_0ba0.kl b/data/keyboards/Vendor_054c_Product_0ba0.kl
new file mode 100644
index 0000000..bc6fc3b
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0.kl
@@ -0,0 +1,70 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 axis
+axis 0x03 LTRIGGER
+# R2 axis
+axis 0x04 RTRIGGER
+
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+# The touchpad for this joystick will become a separate input device in future releases
+# and this button will be equivalent to left mouse button
+# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
new file mode 100644
index 0000000..8b85a38
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
@@ -0,0 +1,67 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+# - Version 0x8111 is for Linux hid-sony driver >=4.10
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 53e9826..fa37bed 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -28,11 +28,10 @@
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
-import android.view.RecordingCanvas;
/**
* This class is a base class for Canvas's drawing operations. Any modifications here
- * should be accompanied by a similar modification to {@link RecordingCanvas}.
+ * should be accompanied by a similar modification to {@link BaseRecordingCanvas}.
*
* The purpose of this class is to minimize the cost of deciding between regular JNI
* and @FastNative JNI to just the virtual call that Canvas already has.
diff --git a/core/java/android/view/RecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
similarity index 97%
rename from core/java/android/view/RecordingCanvas.java
rename to graphics/java/android/graphics/BaseRecordingCanvas.java
index 3364483..6e93691 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -14,25 +14,12 @@
* limitations under the License.
*/
-package android.view;
+package android.graphics;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.graphics.BaseCanvas;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.NinePatch;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Picture;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.TemporaryBuffer;
import android.text.GraphicsOperations;
import android.text.MeasuredParagraph;
import android.text.PrecomputedText;
@@ -49,9 +36,9 @@
*
* @hide
*/
-public class RecordingCanvas extends Canvas {
+public class BaseRecordingCanvas extends Canvas {
- public RecordingCanvas(long nativeCanvas) {
+ public BaseRecordingCanvas(long nativeCanvas) {
super(nativeCanvas);
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cea6c1c..9cbbf4e 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1253,6 +1253,7 @@
final RenderNode node = RenderNode.create("BitmapTemporary", null);
node.setLeftTopRightBottom(0, 0, width, height);
node.setClipToBounds(false);
+ node.setAllowForceDark(false);
final DisplayListCanvas canvas = node.start(width, height);
if (source.getWidth() != width || source.getHeight() != height) {
canvas.scale(width / (float) source.getWidth(),
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 82435d5..21cc375 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -40,17 +40,25 @@
/* Parse fallback list (no names) */
@UnsupportedAppUsage
public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
+ return parse(in, "/system/fonts");
+ }
+
+ /**
+ * Parse the fonts.xml
+ */
+ public static FontConfig parse(InputStream in, String fontDir)
+ throws XmlPullParserException, IOException {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parser.nextTag();
- return readFamilies(parser);
+ return readFamilies(parser, fontDir);
} finally {
in.close();
}
}
- private static FontConfig readFamilies(XmlPullParser parser)
+ private static FontConfig readFamilies(XmlPullParser parser, String fontDir)
throws XmlPullParserException, IOException {
List<FontConfig.Family> families = new ArrayList<>();
List<FontConfig.Alias> aliases = new ArrayList<>();
@@ -60,7 +68,7 @@
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("family")) {
- families.add(readFamily(parser));
+ families.add(readFamily(parser, fontDir));
} else if (tag.equals("alias")) {
aliases.add(readAlias(parser));
} else {
@@ -71,7 +79,10 @@
aliases.toArray(new FontConfig.Alias[aliases.size()]));
}
- private static FontConfig.Family readFamily(XmlPullParser parser)
+ /**
+ * Reads a family element
+ */
+ public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir)
throws XmlPullParserException, IOException {
final String name = parser.getAttributeValue(null, "name");
final String lang = parser.getAttributeValue("", "lang");
@@ -81,7 +92,7 @@
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
final String tag = parser.getName();
if (tag.equals("font")) {
- fonts.add(readFont(parser));
+ fonts.add(readFont(parser, fontDir));
} else {
skip(parser);
}
@@ -102,7 +113,7 @@
private static final Pattern FILENAME_WHITESPACE_PATTERN =
Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
- private static FontConfig.Font readFont(XmlPullParser parser)
+ private static FontConfig.Font readFont(XmlPullParser parser, String fontDir)
throws XmlPullParserException, IOException {
String indexStr = parser.getAttributeValue(null, "index");
int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
@@ -125,7 +136,7 @@
}
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
- return new FontConfig.Font(sanitizedName, index, axes.toArray(
+ return new FontConfig.Font(fontDir + sanitizedName, index, axes.toArray(
new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
}
@@ -137,7 +148,10 @@
return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
}
- private static FontConfig.Alias readAlias(XmlPullParser parser)
+ /**
+ * Reads alias elements
+ */
+ public static FontConfig.Alias readAlias(XmlPullParser parser)
throws XmlPullParserException, IOException {
String name = parser.getAttributeValue(null, "name");
String toName = parser.getAttributeValue(null, "to");
@@ -152,7 +166,10 @@
return new FontConfig.Alias(name, toName, weight);
}
- private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+ /**
+ * Skip until next element
+ */
+ public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
int depth = 1;
while (depth > 0) {
switch (parser.next()) {
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 7bed1ac..6ce66bd 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -187,7 +187,7 @@
* {@link OnHeaderDecodedListener OnHeaderDecodedListener} and once with an
* implementation of {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
* that calls {@link #setTargetSize} with smaller dimensions. One {@code Source}
- * even used simultaneously in multiple threads.</p>
+ * can even be used simultaneously in multiple threads.</p>
*/
public static abstract class Source {
private Source() {}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 33caa00..69ff3bc 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -592,49 +592,6 @@
mShadowLayerColor = paint.mShadowLayerColor;
}
- /**
- * Returns true if all attributes are equal.
- *
- * The caller is expected to have checked the trivial cases, like the pointers being equal,
- * the objects having different classes, or the parameter being null.
- * @hide
- */
- public boolean hasEqualAttributes(@NonNull Paint other) {
- return mColorFilter == other.mColorFilter
- && mMaskFilter == other.mMaskFilter
- && mPathEffect == other.mPathEffect
- && mShader == other.mShader
- && mTypeface == other.mTypeface
- && mXfermode == other.mXfermode
- && mHasCompatScaling == other.mHasCompatScaling
- && mCompatScaling == other.mCompatScaling
- && mInvCompatScaling == other.mInvCompatScaling
- && mBidiFlags == other.mBidiFlags
- && mLocales.equals(other.mLocales)
- && TextUtils.equals(mFontFeatureSettings, other.mFontFeatureSettings)
- && TextUtils.equals(mFontVariationSettings, other.mFontVariationSettings)
- && mShadowLayerRadius == other.mShadowLayerRadius
- && mShadowLayerDx == other.mShadowLayerDx
- && mShadowLayerDy == other.mShadowLayerDy
- && mShadowLayerColor == other.mShadowLayerColor
- && getFlags() == other.getFlags()
- && getHinting() == other.getHinting()
- && getStyle() == other.getStyle()
- && getColor() == other.getColor()
- && getStrokeWidth() == other.getStrokeWidth()
- && getStrokeMiter() == other.getStrokeMiter()
- && getStrokeCap() == other.getStrokeCap()
- && getStrokeJoin() == other.getStrokeJoin()
- && getTextAlign() == other.getTextAlign()
- && isElegantTextHeight() == other.isElegantTextHeight()
- && getTextSize() == other.getTextSize()
- && getTextScaleX() == other.getTextScaleX()
- && getTextSkewX() == other.getTextSkewX()
- && getLetterSpacing() == other.getLetterSpacing()
- && getWordSpacing() == other.getWordSpacing()
- && getHyphenEdit() == other.getHyphenEdit();
- }
-
/** @hide */
@UnsupportedAppUsage
public void setCompatibilityScaling(float factor) {
@@ -1395,6 +1352,38 @@
}
/**
+ * Returns the blur radius of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public float getShadowLayerRadius() {
+ return mShadowLayerRadius;
+ }
+
+ /**
+ * Returns the x offset of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public float getShadowLayerDx() {
+ return mShadowLayerDx;
+ }
+
+ /**
+ * Returns the y offset of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public float getShadowLayerDy() {
+ return mShadowLayerDy;
+ }
+
+ /**
+ * Returns the color of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public @ColorInt int getShadowLayerColor() {
+ return mShadowLayerColor;
+ }
+
+ /**
* Return the paint's Align value for drawing text. This controls how the
* text is positioned relative to its origin. LEFT align means that all of
* the text will be drawn to the right of its origin (i.e. the origin
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 4fec33f..c4dc0ad 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -23,8 +23,11 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -232,6 +235,40 @@
}
/**
+ * Read from a protocol buffer input stream.
+ * Protocol buffer message definition at {@link android.graphics.RectProto}
+ *
+ * @param proto Stream to read the Rect object from.
+ * @param fieldId Field Id of the Rect as defined in the parent message
+ * @hide
+ */
+ public void readFromProto(@NonNull ProtoInputStream proto, long fieldId) throws IOException,
+ WireTypeMismatchException {
+ final long token = proto.start(fieldId);
+ try {
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) RectProto.LEFT:
+ left = proto.readInt(RectProto.LEFT);
+ break;
+ case (int) RectProto.TOP:
+ top = proto.readInt(RectProto.TOP);
+ break;
+ case (int) RectProto.RIGHT:
+ right = proto.readInt(RectProto.RIGHT);
+ break;
+ case (int) RectProto.BOTTOM:
+ bottom = proto.readInt(RectProto.BOTTOM);
+ break;
+ }
+ }
+ } finally {
+ // Let caller handle any exceptions
+ proto.end(token);
+ }
+ }
+
+ /**
* Returns true if the rectangle is empty (left >= right or top >= bottom)
*/
public final boolean isEmpty() {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 492c236..7ad207f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -34,7 +34,6 @@
import android.provider.FontRequest;
import android.provider.FontsContract;
import android.text.FontConfig;
-import android.util.ArrayMap;
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.LruCache;
@@ -1104,6 +1103,9 @@
}
for (FontConfig.Alias alias : aliases) {
+ if (systemFontMap.containsKey(alias.getName())) {
+ continue; // If alias and named family are conflict, use named family.
+ }
final Typeface base = systemFontMap.get(alias.getToName());
final int weight = alias.getWeight();
final Typeface newFace = weight == 400 ? base :
@@ -1112,13 +1114,6 @@
}
}
- // Following methods are left for layoutlib
- // TODO: Remove once layoutlib stop calling buildSystemFallback
- /** @hide */
- public static void buildSystemFallback(String xmlPath, String fontDir,
- ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
- }
-
static {
final HashMap<String, Typeface> systemFontMap = new HashMap<>();
initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 76f2cfb..6c1372f 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -51,6 +51,7 @@
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.DisplayListCanvas;
+import android.view.NativeVectorDrawableAnimator;
import android.view.RenderNode;
import android.view.RenderNodeAnimatorSetHelper;
import android.view.View;
@@ -1231,7 +1232,8 @@
/**
* @hide
*/
- public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator {
+ public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator,
+ NativeVectorDrawableAnimator {
private static final int START_ANIMATION = 1;
private static final int REVERSE_ANIMATION = 2;
private static final int RESET_ANIMATION = 3;
@@ -1704,6 +1706,7 @@
}
}
+ @Override
public long getAnimatorNativePtr() {
return mSetPtr;
}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 1458c66..bd1ac25 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -422,9 +422,10 @@
nAddAxis(builderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
}
}
- final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex);
- final Font font = new Font(ptr, mBuffer, mFile, mWeight, italic, mTtcIndex, mAxes,
- mLocaleList);
+ final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer();
+ final long ptr = nBuild(builderPtr, readonlyBuffer, mWeight, italic, mTtcIndex);
+ final Font font = new Font(ptr, readonlyBuffer, mFile, mWeight, italic, mTtcIndex,
+ mAxes, mLocaleList);
sFontRegistory.registerNativeAllocation(font, ptr);
return font;
}
@@ -477,7 +478,7 @@
}
/**
- * Retuns a font file buffer.
+ * Returns a font file buffer.
*
* @return a font buffer
*/
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
new file mode 100644
index 0000000..0291d74
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.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 android.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.graphics.FontListParser;
+import android.text.FontConfig;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Parser for font customization
+ *
+ * @hide
+ */
+public class FontCustomizationParser {
+ /**
+ * Represents a customization XML
+ */
+ public static class Result {
+ ArrayList<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>();
+ ArrayList<FontConfig.Alias> mAdditionalAliases = new ArrayList<>();
+ }
+
+ /**
+ * Parses the customization XML
+ *
+ * Caller must close the input stream
+ */
+ public static Result parse(@NonNull InputStream in, @NonNull String fontDir)
+ throws XmlPullParserException, IOException {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parser.nextTag();
+ return readFamilies(parser, fontDir);
+ }
+
+ private static void validate(Result result) {
+ HashSet<String> familyNames = new HashSet<>();
+ for (int i = 0; i < result.mAdditionalNamedFamilies.size(); ++i) {
+ final FontConfig.Family family = result.mAdditionalNamedFamilies.get(i);
+ final String name = family.getName();
+ if (name == null) {
+ throw new IllegalArgumentException("new-named-family requires name attribute");
+ }
+ if (!familyNames.add(name)) {
+ throw new IllegalArgumentException(
+ "new-named-family requires unique name attribute");
+ }
+ }
+ }
+
+ private static Result readFamilies(XmlPullParser parser, String fontDir)
+ throws XmlPullParserException, IOException {
+ Result out = new Result();
+ parser.require(XmlPullParser.START_TAG, null, "fonts-modification");
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+ String tag = parser.getName();
+ if (tag.equals("family")) {
+ readFamily(parser, fontDir, out);
+ } else if (tag.equals("alias")) {
+ out.mAdditionalAliases.add(FontListParser.readAlias(parser));
+ } else {
+ FontListParser.skip(parser);
+ }
+ }
+ validate(out);
+ return out;
+ }
+
+ private static void readFamily(XmlPullParser parser, String fontDir, Result out)
+ throws XmlPullParserException, IOException {
+ final String customizationType = parser.getAttributeValue(null, "customizationType");
+ if (customizationType == null) {
+ throw new IllegalArgumentException("customizationType must be specified");
+ }
+ if (customizationType.equals("new-named-family")) {
+ out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir));
+ } else {
+ throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index f4a2199..2d21bbb 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -45,7 +45,7 @@
/**
* Provides the system font configurations.
*/
-public class SystemFonts {
+public final class SystemFonts {
private static final String TAG = "SystemFonts";
private static final String DEFAULT_FAMILY = "sans-serif";
@@ -58,8 +58,7 @@
/**
* Returns all available font files in the system.
*
- * Note: The order of this font doesn't indicates anything.
- * @return an array of system fonts
+ * @return a set of system fonts
*/
public static @NonNull Set<Font> getAvailableFonts() {
HashSet<Font> set = new HashSet<>();
@@ -114,7 +113,6 @@
private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
@NonNull Map<String, ByteBuffer> cache,
- @NonNull String fontDir,
@NonNull ArrayList<Font> availableFonts) {
final String languageTags = xmlFamily.getLanguages();
@@ -139,8 +137,7 @@
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
- xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir,
- availableFonts);
+ xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
@@ -152,7 +149,7 @@
}
} else {
final FontFamily family = createFontFamily(
- xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir,
+ xmlFamily.getName(), fallback, languageTags, variant, cache,
availableFonts);
if (family != null) {
fallbackMap.valueAt(i).add(family);
@@ -170,7 +167,6 @@
@NonNull String languageTags,
@FontConfig.Family.Variant int variant,
@NonNull Map<String, ByteBuffer> cache,
- @NonNull String fontDir,
@NonNull ArrayList<Font> availableFonts) {
if (fonts.size() == 0) {
return null;
@@ -179,7 +175,7 @@
FontFamily.Builder b = null;
for (int i = 0; i < fonts.size(); i++) {
final FontConfig.Font fontConfig = fonts.get(i);
- final String fullPath = fontDir + fontConfig.getFontName();
+ final String fullPath = fontConfig.getFontName();
ByteBuffer buffer = cache.get(fullPath);
if (buffer == null) {
if (cache.containsKey(fullPath)) {
@@ -214,6 +210,22 @@
return b == null ? null : b.build(languageTags, variant);
}
+ private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily,
+ @NonNull HashMap<String, ByteBuffer> bufferCache,
+ @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap,
+ @NonNull ArrayList<Font> availableFonts) {
+ final String familyName = xmlFamily.getName();
+ final FontFamily family = createFontFamily(
+ familyName, Arrays.asList(xmlFamily.getFonts()),
+ xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts);
+ if (family == null) {
+ return;
+ }
+ final ArrayList<FontFamily> fallback = new ArrayList<>();
+ fallback.add(family);
+ fallbackListMap.put(familyName, fallback);
+ }
+
/**
* Build the system fallback from xml file.
*
@@ -227,11 +239,12 @@
@VisibleForTesting
public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
@NonNull String fontDir,
+ @NonNull FontCustomizationParser.Result oemCustomization,
@NonNull ArrayMap<String, FontFamily[]> fallbackMap,
@NonNull ArrayList<Font> availableFonts) {
try {
final FileInputStream fontsIn = new FileInputStream(xmlPath);
- final FontConfig fontConfig = FontListParser.parse(fontsIn);
+ final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir);
final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>();
final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies();
@@ -243,16 +256,12 @@
if (familyName == null) {
continue;
}
- final FontFamily family = createFontFamily(
- xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
- xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir,
- availableFonts);
- if (family == null) {
- continue;
- }
- final ArrayList<FontFamily> fallback = new ArrayList<>();
- fallback.add(family);
- fallbackListMap.put(familyName, fallback);
+ appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts);
+ }
+
+ for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) {
+ appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i),
+ bufferCache, fallbackListMap, availableFonts);
}
// Then, add fallback fonts to the each fallback map.
@@ -261,8 +270,7 @@
// The first family (usually the sans-serif family) is always placed immediately
// after the primary family in the fallback.
if (i == 0 || xmlFamily.getName() == null) {
- pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir,
- availableFonts);
+ pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, availableFonts);
}
}
@@ -275,20 +283,36 @@
fallbackMap.put(fallbackName, families);
}
- return fontConfig.getAliases();
+ final ArrayList<FontConfig.Alias> list = new ArrayList<>();
+ list.addAll(Arrays.asList(fontConfig.getAliases()));
+ list.addAll(oemCustomization.mAdditionalAliases);
+ return list.toArray(new FontConfig.Alias[list.size()]);
} catch (IOException | XmlPullParserException e) {
Log.e(TAG, "Failed initialize system fallbacks.", e);
return ArrayUtils.emptyArray(FontConfig.Alias.class);
}
}
+ private static FontCustomizationParser.Result readFontCustomization(
+ @NonNull String customizeXml, @NonNull String customFontsDir) {
+ try (FileInputStream f = new FileInputStream(customizeXml)) {
+ return FontCustomizationParser.parse(f, customFontsDir);
+ } catch (IOException e) {
+ return new FontCustomizationParser.Result();
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Failed to parse font customization XML", e);
+ return new FontCustomizationParser.Result();
+ }
+ }
+
static {
final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
final ArrayList<Font> availableFonts = new ArrayList<>();
+ final FontCustomizationParser.Result oemCustomization =
+ readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/");
sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
- systemFallbackMap, availableFonts);
+ oemCustomization, systemFallbackMap, availableFonts);
sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
sAvailableFonts = Collections.unmodifiableList(availableFonts);
}
-
}
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index e4cd6a8..6c9eee0 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -47,11 +47,11 @@
constexpr unique_cptr() : ptr_(nullptr) {}
constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
- unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
+ unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
- inline unique_cptr& operator=(unique_cptr&& o) {
+ inline unique_cptr& operator=(unique_cptr&& o) noexcept {
if (&o == this) {
return *this;
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 11dad2e..494e513 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -229,6 +229,7 @@
"ResourceCache.cpp",
"SkiaCanvas.cpp",
"Snapshot.cpp",
+ "TreeInfo.cpp",
"VectorDrawable.cpp",
"protos/graphicsstats.proto",
],
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index adcdc18..06e937a 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -28,6 +28,7 @@
#include <cmath>
#include <log/log.h>
+#include <SkHighContrastFilter.h>
namespace android::uirenderer {
@@ -78,7 +79,6 @@
info.fColors = _colorStorage.data();
info.fColorOffsets = _offsetStorage.data();
SkShader::GradientType type = paint.getShader()->asAGradient(&info);
- ALOGW_IF(type, "Found gradient of type = %d", type);
if (info.fColorCount <= 10) {
switch (type) {
@@ -107,10 +107,43 @@
}
}
+static BitmapPalette paletteForColorHSV(SkColor color) {
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ return hsv[2] >= .5f ? BitmapPalette::Light : BitmapPalette::Dark;
+}
+
+static BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette) {
+ if (palette == BitmapPalette::Unknown || !paint || !paint->getColorFilter()) {
+ return palette;
+ }
+
+ SkColor color = palette == BitmapPalette::Light ? SK_ColorWHITE : SK_ColorBLACK;
+ color = paint->getColorFilter()->filterColor(color);
+ return paletteForColorHSV(color);
+}
+
bool transformPaint(ColorTransform transform, SkPaint* paint) {
// TODO
applyColorTransform(transform, *paint);
return true;
}
+bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) {
+ palette = filterPalette(paint, palette);
+ bool shouldInvert = false;
+ if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) {
+ shouldInvert = true;
+ }
+ if (palette == BitmapPalette::Dark && transform == ColorTransform::Light) {
+ shouldInvert = true;
+ }
+ if (shouldInvert) {
+ SkHighContrastConfig config;
+ config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+ paint->setColorFilter(SkHighContrastFilter::Make(config)->makeComposed(paint->refColorFilter()));
+ }
+ return shouldInvert;
+}
+
}; // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
index 32d9a05..e723d64 100644
--- a/libs/hwui/CanvasTransform.h
+++ b/libs/hwui/CanvasTransform.h
@@ -16,6 +16,8 @@
#pragma once
+#include "hwui/Bitmap.h"
+
#include <SkCanvas.h>
#include <SkPaintFilterCanvas.h>
@@ -26,6 +28,7 @@
enum class UsageHint {
Unknown = 0,
Background = 1,
+ Foreground = 2,
};
enum class ColorTransform {
@@ -37,4 +40,6 @@
// True if the paint was modified, false otherwise
bool transformPaint(ColorTransform transform, SkPaint* paint);
+bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette);
+
} // namespace android::uirenderer;
\ No newline at end of file
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 837d546..b772e5b 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -29,12 +29,16 @@
, mGLContextAttached(false)
, mUpdateTexImage(false)
, mLayer(nullptr) {
- renderState.registerDeferredLayerUpdater(this);
+ renderState.registerContextCallback(this);
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
setTransform(nullptr);
- mRenderState.unregisterDeferredLayerUpdater(this);
+ mRenderState.removeContextCallback(this);
+ destroyLayer();
+}
+
+void DeferredLayerUpdater::onContextDestroyed() {
destroyLayer();
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 4c323b8..b2c5131 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -27,6 +27,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include "renderstate/RenderState.h"
#include "surfacetexture/SurfaceTexture.h"
#include "Layer.h"
#include "Rect.h"
@@ -38,7 +39,7 @@
// Container to hold the properties a layer should be set to at the start
// of a render pass
-class DeferredLayerUpdater : public VirtualLightRefBase {
+class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallback {
public:
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
@@ -98,6 +99,9 @@
void destroyLayer();
+protected:
+ void onContextDestroyed() override;
+
private:
RenderState& mRenderState;
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 21fbbdc..0b9d82b 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -61,18 +61,6 @@
return displayInfo;
}
-void QueryCompositionPreference(ui::Dataspace* dataSpace,
- ui::PixelFormat* pixelFormat) {
- if (Properties::isolatedProcess) {
- *dataSpace = ui::Dataspace::V0_SRGB;
- *pixelFormat = ui::PixelFormat::RGBA_8888;
- }
-
- status_t status =
- SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat);
- LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status);
-}
-
DeviceInfo::DeviceInfo() {
#if HWUI_NULL_GPU
mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
@@ -80,7 +68,6 @@
mMaxTextureSize = -1;
#endif
mDisplayInfo = QueryDisplayInfo();
- QueryCompositionPreference(&mTargetDataSpace, &mTargetPixelFormat);
}
int DeviceInfo::maxTextureSize() const {
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 1d747741..5956215 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -17,7 +17,6 @@
#define DEVICEINFO_H
#include <ui/DisplayInfo.h>
-#include <ui/GraphicTypes.h>
#include "utils/Macros.h"
@@ -37,9 +36,6 @@
// this value is only valid after the GPU has been initialized and there is a valid graphics
// context or if you are using the HWUI_NULL_GPU
int maxTextureSize() const;
-
- ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; }
- ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; }
const DisplayInfo& displayInfo() const { return mDisplayInfo; }
private:
@@ -50,10 +46,6 @@
int mMaxTextureSize;
DisplayInfo mDisplayInfo;
-
- // TODO(lpy) Replace below with android_ prefix types.
- ui::Dataspace mTargetDataSpace;
- ui::PixelFormat mTargetPixelFormat;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index f61c156..04cf611 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -49,4 +49,5 @@
X(DrawPoints)
X(DrawVertices)
X(DrawAtlas)
-X(DrawShadowRec)
\ No newline at end of file
+X(DrawShadowRec)
+X(DrawVectorDrawable)
\ No newline at end of file
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e6d2a6f..f2d50cd 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -146,7 +146,7 @@
frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
// If we hit the deadline, cool!
- if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+ if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline || totalDuration < mFrameInterval) {
if (isTripleBuffered) {
mData->reportJankType(JankType::kHighInputLatency);
(*mGlobalData)->reportJankType(JankType::kHighInputLatency);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 17bec19..a699e2f 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -61,6 +61,7 @@
bool Properties::disableVsync = false;
bool Properties::skpCaptureEnabled = false;
bool Properties::forceDarkMode = false;
+bool Properties::enableForceDarkSupport = false;
bool Properties::enableRTAnimations = true;
bool Properties::runningInEmulator = false;
@@ -149,6 +150,9 @@
forceDarkMode = property_get_bool(PROPERTY_FORCE_DARK, false);
+ // TODO: make this on by default
+ enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, false);
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
(prevDebugStencilClip != debugStencilClip);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index ea017a7..542bc71 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -192,6 +192,8 @@
#define PROPERTY_FORCE_DARK "debug.hwui.force_dark"
+#define PROPERTY_ENABLE_FORCE_DARK "debug.hwui.force_dark_enabled"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -266,6 +268,7 @@
static bool skpCaptureEnabled;
static bool forceDarkMode;
+ static bool enableForceDarkSupport;
// For experimentation b/68769804
ANDROID_API static bool enableRTAnimations;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 3eaff03..c30af84 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -16,6 +16,8 @@
#include "RecordingCanvas.h"
+#include "VectorDrawable.h"
+
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
@@ -278,8 +280,8 @@
struct DrawImage final : Op {
static const auto kType = Type::DrawImage;
- DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint)
- : image(std::move(image)), x(x), y(y) {
+ DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette)
+ : image(std::move(image)), x(x), y(y), palette(palette) {
if (paint) {
this->paint = *paint;
}
@@ -287,6 +289,7 @@
sk_sp<const SkImage> image;
SkScalar x, y;
SkPaint paint;
+ BitmapPalette palette;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); }
};
struct DrawImageNine final : Op {
@@ -309,8 +312,8 @@
struct DrawImageRect final : Op {
static const auto kType = Type::DrawImageRect;
DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SkCanvas::SrcRectConstraint constraint)
- : image(std::move(image)), dst(dst), constraint(constraint) {
+ const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, BitmapPalette palette)
+ : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
if (paint) {
this->paint = *paint;
@@ -320,6 +323,7 @@
SkRect src, dst;
SkPaint paint;
SkCanvas::SrcRectConstraint constraint;
+ BitmapPalette palette;
void draw(SkCanvas* c, const SkMatrix&) const {
c->drawImageRect(image.get(), src, dst, &paint, constraint);
}
@@ -496,6 +500,27 @@
SkDrawShadowRec fRec;
void draw(SkCanvas* c, const SkMatrix&) const { c->private_draw_shadow_rec(fPath, fRec); }
};
+
+struct DrawVectorDrawable final : Op {
+ static const auto kType = Type::DrawVectorDrawable;
+ DrawVectorDrawable(VectorDrawableRoot* tree)
+ : mRoot(tree)
+ , mBounds(tree->stagingProperties().getBounds())
+ , palette(tree->computePalette()) {
+ // Recording, so use staging properties
+ tree->getPaintFor(&paint, tree->stagingProperties());
+ }
+
+ void draw(SkCanvas* canvas, const SkMatrix&) const {
+ mRoot->draw(canvas, mBounds, paint);
+ }
+
+ sp<VectorDrawableRoot> mRoot;
+ SkRect mBounds;
+ SkPaint paint;
+ BitmapPalette palette;
+};
+
}
template <typename T, typename... Args>
@@ -609,8 +634,8 @@
this->push<DrawPicture>(0, picture, matrix, paint);
}
void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y,
- const SkPaint* paint) {
- this->push<DrawImage>(0, std::move(image), x, y, paint);
+ const SkPaint* paint, BitmapPalette palette) {
+ this->push<DrawImage>(0, std::move(image), x, y, paint, palette);
}
void DisplayListData::drawImageNine(sk_sp<const SkImage> image, const SkIRect& center,
const SkRect& dst, const SkPaint* paint) {
@@ -618,8 +643,8 @@
}
void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* src,
const SkRect& dst, const SkPaint* paint,
- SkCanvas::SrcRectConstraint constraint) {
- this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint);
+ SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) {
+ this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
}
void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
@@ -638,28 +663,33 @@
const SkPaint& paint) {
void* pod = this->push<DrawText>(bytes, bytes, x, y, paint);
copy_v(pod, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawPosText(const void* text, size_t bytes, const SkPoint pos[],
const SkPaint& paint) {
int n = paint.countText(text, bytes);
void* pod = this->push<DrawPosText>(n * sizeof(SkPoint) + bytes, bytes, paint, n);
copy_v(pod, pos, n, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawPosTextH(const void* text, size_t bytes, const SkScalar xs[], SkScalar y,
const SkPaint& paint) {
int n = paint.countText(text, bytes);
void* pod = this->push<DrawPosTextH>(n * sizeof(SkScalar) + bytes, bytes, y, paint, n);
copy_v(pod, xs, n, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawTextRSXform(const void* text, size_t bytes, const SkRSXform xforms[],
const SkRect* cull, const SkPaint& paint) {
int n = paint.countText(text, bytes);
void* pod = this->push<DrawTextRSXform>(bytes + n * sizeof(SkRSXform), bytes, n, cull, paint);
copy_v(pod, xforms, n, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
this->push<DrawTextBlob>(0, blob, x, y, paint);
+ mHasText = true;
}
void DisplayListData::drawPatch(const SkPoint points[12], const SkColor colors[4],
@@ -691,6 +721,9 @@
void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
this->push<DrawShadowRec>(0, path, rec);
}
+void DisplayListData::drawVectorDrawable(VectorDrawableRoot* tree) {
+ this->push<DrawVectorDrawable>(0, tree);
+}
typedef void (*draw_fn)(const void*, SkCanvas*, const SkMatrix&);
typedef void (*void_fn)(const void*);
@@ -733,20 +766,35 @@
}
template <class T>
-using has_paint_t = decltype(std::declval<T>().paint);
+using has_paint_helper = decltype(std::declval<T>().paint);
+
+template <class T>
+constexpr bool has_paint = std::experimental::is_detected_v<has_paint_helper, T>;
+
+template <class T>
+using has_palette_helper = decltype(std::declval<T>().palette);
+
+template <class T>
+constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper, T>;
template <class T>
constexpr color_transform_fn colorTransformForOp() {
- if
- constexpr(std::experimental::is_detected_v<has_paint_t, T>) {
- return [](const void* op, ColorTransform transform) {
- // TODO: We should be const. Or not. Or just use a different map
- // Unclear, but this is the quick fix
- transformPaint(transform,
- const_cast<SkPaint*>(&(reinterpret_cast<const T*>(op)->paint)));
- };
- }
- else {
+ if constexpr(has_paint<T> && has_palette<T>) {
+ // It's a bitmap
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ };
+ } else if constexpr(has_paint<T>) {
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ };
+ } else {
return nullptr;
}
}
@@ -875,7 +923,7 @@
void RecordingCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y,
const SkPaint* paint) {
- fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint);
+ fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, const SkRect& dst,
const SkPaint* paint) {
@@ -883,16 +931,26 @@
}
void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) {
- fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint);
+ fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint);
}
+void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
+ const SkPaint* paint, BitmapPalette palette) {
+ fDL->drawImage(image, x, y, paint, palette);
+}
+
+void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette) {
+ fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
+}
+
void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
const SkPaint* paint) {
- fDL->drawImage(sk_ref_sp(img), x, y, paint);
+ fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawImageNine(const SkImage* img, const SkIRect& center, const SkRect& dst,
const SkPaint* paint) {
@@ -900,7 +958,7 @@
}
void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) {
- fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint);
+ fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
@@ -930,5 +988,9 @@
fDL->drawShadowRec(path, rec);
}
+void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+ fDL->drawVectorDrawable(tree);
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 32ce1d3..80c80ca 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -17,6 +17,7 @@
#pragma once
#include "CanvasTransform.h"
+#include "hwui/Bitmap.h"
#include "hwui/Canvas.h"
#include "utils/Macros.h"
#include "utils/TypeLogic.h"
@@ -53,6 +54,7 @@
class DisplayListData final {
public:
+ DisplayListData() : mHasText(false) {}
~DisplayListData();
void draw(SkCanvas* canvas) const;
@@ -62,6 +64,8 @@
void applyColorTransform(ColorTransform transform);
+ bool hasText() const { return mHasText; }
+
private:
friend class RecordingCanvas;
@@ -101,10 +105,10 @@
void drawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, const SkPaint&);
void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&);
- void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*);
+ void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette);
void drawImageNine(sk_sp<const SkImage>, const SkIRect&, const SkRect&, const SkPaint*);
void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
- SkCanvas::SrcRectConstraint);
+ SkCanvas::SrcRectConstraint, BitmapPalette palette);
void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
const SkPaint*);
@@ -116,6 +120,7 @@
void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
SkBlendMode, const SkRect*, const SkPaint*);
void drawShadowRec(const SkPath&, const SkDrawShadowRec&);
+ void drawVectorDrawable(VectorDrawableRoot* tree);
template <typename T, typename... Args>
void* push(size_t, Args&&...);
@@ -126,6 +131,8 @@
SkAutoTMalloc<uint8_t> fBytes;
size_t fUsed = 0;
size_t fReserved = 0;
+
+ bool mHasText : 1;
};
class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
@@ -178,6 +185,12 @@
void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
SrcRectConstraint) override;
+ void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top,
+ const SkPaint* paint, BitmapPalette pallete);
+
+ void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+
void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override;
@@ -193,6 +206,8 @@
SkBlendMode, const SkRect*, const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
+ void drawVectorDrawable(VectorDrawableRoot* tree);
+
private:
typedef SkCanvasVirtualEnforcer<SkNoDrawCanvas> INHERITED;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d5afb20..d9a7cc3 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -112,7 +112,9 @@
LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
MarkAndSweepRemoved observer(&info);
+ const int before = info.disableForceDark;
prepareTreeImpl(observer, info, false);
+ LOG_ALWAYS_FATAL_IF(before != info.disableForceDark, "Mis-matched force dark");
}
void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
@@ -158,7 +160,7 @@
CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
CC_UNLIKELY(!properties().fitsOnLayer())) {
if (CC_UNLIKELY(hasLayer())) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
return;
}
@@ -195,6 +197,11 @@
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingPropertiesChanges(info);
}
+
+ if (!mProperties.getAllowForceDark()) {
+ info.disableForceDark++;
+ }
+
uint32_t animatorDirtyMask = 0;
if (CC_LIKELY(info.runAnimations)) {
animatorDirtyMask = mAnimatorManager.animate(info);
@@ -232,6 +239,9 @@
}
pushLayerUpdate(info);
+ if (!mProperties.getAllowForceDark()) {
+ info.disableForceDark--;
+ }
info.damageAccumulator->popTransform();
}
@@ -272,8 +282,12 @@
mStagingDisplayList = nullptr;
if (mDisplayList) {
mDisplayList->syncContents();
- if (CC_UNLIKELY(Properties::forceDarkMode)) {
+
+ if (CC_UNLIKELY(info && !info->disableForceDark)) {
auto usage = usageHint();
+ if (mDisplayList->hasText()) {
+ usage = UsageHint::Foreground;
+ }
if (usage == UsageHint::Unknown) {
if (mDisplayList->mChildNodes.size() > 1) {
usage = UsageHint::Background;
@@ -313,7 +327,7 @@
void RenderNode::destroyHardwareResources(TreeInfo* info) {
if (hasLayer()) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
setStagingDisplayList(nullptr);
@@ -323,7 +337,7 @@
void RenderNode::destroyLayers() {
if (hasLayer()) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
if (mDisplayList) {
mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); });
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 83b0c22..211dd2d 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -107,7 +107,7 @@
bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
bool hasProjectionReceiver() const {
- return mDisplayList && mDisplayList->projectionReceiveIndex >= 0;
+ return mDisplayList && mDisplayList->containsProjectionReceiver();
}
const char* getName() const { return mName.string(); }
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 7966845..04379ae 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -535,6 +535,14 @@
return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
}
+ bool setAllowForceDark(bool allow) {
+ return RP_SET(mPrimitiveFields.mAllowForceDark, allow);
+ }
+
+ bool getAllowForceDark() const {
+ return mPrimitiveFields.mAllowForceDark;
+ }
+
private:
// Rendering properties
struct PrimitiveFields {
@@ -554,6 +562,7 @@
bool mMatrixOrPivotDirty = false;
bool mProjectBackwards = false;
bool mProjectionReceiver = false;
+ bool mAllowForceDark = true;
Rect mClipBounds;
Outline mOutline;
RevealClip mRevealClip;
diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp
new file mode 100644
index 0000000..808a12a
--- /dev/null
+++ b/libs/hwui/TreeInfo.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "TreeInfo.h"
+
+#include "renderthread/CanvasContext.h"
+
+namespace android::uirenderer {
+
+TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext)
+ : mode(mode)
+ , prepareTextures(mode == MODE_FULL)
+ , canvasContext(canvasContext)
+ , disableForceDark(canvasContext.useForceDark() ? 0 : 1) {}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index f2766d6..a0d9605 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,6 +16,7 @@
#pragma once
+#include "Properties.h"
#include "utils/Macros.h"
#include <utils/Timers.h>
@@ -39,7 +40,7 @@
virtual void onError(const std::string& message) = 0;
protected:
- virtual ~ErrorHandler() {}
+ virtual ~ErrorHandler() = default;
};
class TreeObserver {
@@ -51,7 +52,7 @@
virtual void onMaybeRemovedFromTree(RenderNode* node) = 0;
protected:
- virtual ~TreeObserver() {}
+ virtual ~TreeObserver() = default;
};
// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
@@ -70,8 +71,7 @@
MODE_RT_ONLY,
};
- TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext)
- : mode(mode), prepareTextures(mode == MODE_FULL), canvasContext(canvasContext) {}
+ TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext);
TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
@@ -93,6 +93,8 @@
bool updateWindowPositions = false;
+ int disableForceDark;
+
struct Out {
bool hasFunctors = false;
// This is only updated if evaluateAnimations is true
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 9f82d0f..dbbe9f3 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -458,29 +458,22 @@
mStagingCache.dirty = false;
}
- SkPaint tmpPaint;
- SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
+ SkPaint paint;
+ getPaintFor(&paint, mStagingProperties);
outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(),
mStagingProperties.getBounds().top(),
mStagingProperties.getBounds().right(),
- mStagingProperties.getBounds().bottom(), paint);
+ mStagingProperties.getBounds().bottom(), &paint);
}
-SkPaint* Tree::getPaint() {
- return updatePaint(&mPaint, &mProperties);
-}
-
-// Update the given paint with alpha and color filter. Return nullptr if no color filter is
-// specified and root alpha is 1. Otherwise, return updated paint.
-SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
+void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const {
// HWUI always draws VD with bilinear filtering.
outPaint->setFilterQuality(kLow_SkFilterQuality);
- if (prop->getRootAlpha() < 1.0f || prop->getColorFilter() != nullptr) {
- outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
- outPaint->setAlpha(prop->getRootAlpha() * 255);
+ if (prop.getRootAlpha() < 1.0f || prop.getColorFilter() != nullptr) {
+ outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
+ outPaint->setAlpha(prop.getRootAlpha() * 255);
}
- return outPaint;
}
Bitmap& Tree::getBitmapUpdateIfDirty() {
@@ -553,11 +546,15 @@
mAtlasKey = INVALID_ATLAS_KEY;
}
-void Tree::draw(SkCanvas* canvas, const SkRect& bounds) {
+void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) {
+ // Update the paint for any animatable properties
+ SkPaint paint = inPaint;
+ paint.setAlpha(mProperties.getRootAlpha() * 255);
+
SkRect src;
sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
if (vdSurface) {
- canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, getPaint(),
+ canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
// Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
@@ -570,7 +567,7 @@
int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
- getPaint(), SkCanvas::kFast_SrcRectConstraint);
+ &paint, SkCanvas::kFast_SrcRectConstraint);
mCache.clear();
markDirty();
}
@@ -620,6 +617,80 @@
}
}
+class MinMaxAverage {
+public:
+ void add(float sample) {
+ if (mCount == 0) {
+ mMin = sample;
+ mMax = sample;
+ } else {
+ mMin = std::min(mMin, sample);
+ mMax = std::max(mMax, sample);
+ }
+ mTotal += sample;
+ mCount++;
+ }
+
+ float average() { return mTotal / mCount; }
+
+ float min() { return mMin; }
+
+ float max() { return mMax; }
+
+ float delta() { return mMax - mMin; }
+
+private:
+ float mMin = 0.0f;
+ float mMax = 0.0f;
+ float mTotal = 0.0f;
+ int mCount = 0;
+};
+
+BitmapPalette Tree::computePalette() {
+ // TODO Cache this and share the code with Bitmap.cpp
+
+ ATRACE_CALL();
+
+ // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+ // Experiment with something simpler since we just want to figure out if it's "color-ful"
+ // and then the average perceptual lightness.
+
+ MinMaxAverage hue, saturation, value;
+ int sampledCount = 0;
+
+ // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+ mRootNode->forEachFillColor([&](SkColor color) {
+ if (SkColorGetA(color) < 75) {
+ return;
+ }
+ sampledCount++;
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ hue.add(hsv[0]);
+ saturation.add(hsv[1]);
+ value.add(hsv[2]);
+ });
+
+ if (sampledCount == 0) {
+ ALOGV("VectorDrawable is mostly translucent");
+ return BitmapPalette::Unknown;
+ }
+
+ ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = "
+ "%f]; value [min = %f, max = %f, avg = %f]",
+ sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(),
+ saturation.average(), value.min(), value.max(), value.average());
+
+ if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+ if (value.average() >= .5f) {
+ return BitmapPalette::Light;
+ } else {
+ return BitmapPalette::Dark;
+ }
+ }
+ return BitmapPalette::Unknown;
+}
+
}; // namespace VectorDrawable
}; // namespace uirenderer
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index af0ae05..9c0bb16 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -120,6 +120,8 @@
virtual void syncProperties() = 0;
virtual void setAntiAlias(bool aa) = 0;
+ virtual void forEachFillColor(const std::function<void(SkColor)>& func) const { }
+
protected:
std::string mName;
PropertyChangedListener* mPropertyChangedListener = nullptr;
@@ -349,6 +351,9 @@
}
}
virtual void setAntiAlias(bool aa) { mAntiAlias = aa; }
+ void forEachFillColor(const std::function<void(SkColor)>& func) const override {
+ func(mStagingProperties.getFillColor());
+ }
protected:
const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
@@ -480,6 +485,12 @@
}
}
+ void forEachFillColor(const std::function<void(SkColor)>& func) const override {
+ for (auto& child : mChildren) {
+ child->forEachFillColor(func);
+ }
+ }
+
private:
GroupProperties mProperties = GroupProperties(this);
GroupProperties mStagingProperties = GroupProperties(this);
@@ -495,8 +506,8 @@
// Copy properties from the tree and use the give node as the root node
Tree(const Tree* copy, Group* rootNode) : Tree(rootNode) {
- mStagingProperties.syncAnimatableProperties(*copy->stagingProperties());
- mStagingProperties.syncNonAnimatableProperties(*copy->stagingProperties());
+ mStagingProperties.syncAnimatableProperties(copy->stagingProperties());
+ mStagingProperties.syncNonAnimatableProperties(copy->stagingProperties());
}
// Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input
// canvas. Returns the number of pixels needed for the bitmap cache.
@@ -506,7 +517,6 @@
Bitmap& getBitmapUpdateIfDirty();
void setAllowCaching(bool allowCaching) { mAllowCaching = allowCaching; }
- SkPaint* getPaint();
void syncProperties() {
if (mStagingProperties.mNonAnimatablePropertiesDirty) {
mCache.dirty |= (mProperties.mNonAnimatableProperties.viewportWidth !=
@@ -618,7 +628,7 @@
};
void onPropertyChanged(TreeProperties* prop);
TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
- const TreeProperties* stagingProperties() const { return &mStagingProperties; }
+ const TreeProperties& stagingProperties() const { return mStagingProperties; }
// This should only be called from animations on RT
TreeProperties* mutateProperties() { return &mProperties; }
@@ -636,7 +646,10 @@
* Draws VD cache into a canvas. This should always be called from RT and it works with Skia
* pipelines only.
*/
- void draw(SkCanvas* canvas, const SkRect& bounds);
+ void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint);
+
+ void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
+ BitmapPalette computePalette();
/**
* Draws VD into a GPU backed surface.
@@ -680,7 +693,6 @@
skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY;
};
- SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
bool canReuseBitmap(Bitmap*, int width, int height);
void updateBitmapCache(Bitmap& outCache, bool useStagingData);
@@ -696,8 +708,6 @@
TreeProperties mProperties = TreeProperties(this);
TreeProperties mStagingProperties = TreeProperties(this);
- SkPaint mPaint;
-
Cache mStagingCache;
Cache mCache;
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 8a44b8b..f0aa35a 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -106,7 +106,7 @@
Snapshot decodeNextFrame();
Snapshot reset();
- size_t byteSize() const { return sizeof(this) + mBytesUsed; }
+ size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
protected:
virtual void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 440620a..9c28453 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -230,8 +230,6 @@
mPixelStorage.hardware.buffer = nullptr;
break;
}
-
- android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID());
}
bool Bitmap::hasHardwareMipMap() const {
@@ -330,15 +328,6 @@
if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
*outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
}
-
- // TODO: Move this to the canvas (or other?) layer where we have the target lightness
- // mode and can selectively do the right thing.
- // if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) {
- // SkHighContrastConfig config;
- // config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
- // *outputColorFilter =
- // SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter);
- // }
return image;
}
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 170335d..43d457a 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,6 +34,8 @@
Hardware,
};
+// TODO: Find a better home for this. It's here because hwui/Bitmap is exported and CanvasTransform
+// isn't, but cleanup should be done
enum class BitmapPalette {
Unknown,
Light,
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 78b64b2..3890513 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -132,7 +132,6 @@
mChildFunctors.clear();
mChildNodes.clear();
- projectionReceiveIndex = -1;
allocator.~LinearAllocator();
new (&allocator) LinearAllocator();
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 4c78539..ac7bb7b 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -49,9 +49,6 @@
*/
class SkiaDisplayList {
public:
- // index of DisplayListOp restore, after which projected descendants should be drawn
- int projectionReceiveIndex = -1;
-
size_t getUsedSize() { return allocator.usedSize(); }
~SkiaDisplayList() {
@@ -96,6 +93,8 @@
*/
bool hasVectorDrawables() const { return !mVectorDrawables.empty(); }
+ bool hasText() const { return mDisplayList.hasText(); }
+
/**
* Attempts to reset and reuse this DisplayList.
*
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 3b1ebd6..d401b38 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -21,6 +21,7 @@
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
#include "hwui/Bitmap.h"
+#include "private/hwui/DrawGlInfo.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "renderthread/Frame.h"
@@ -43,7 +44,13 @@
namespace skiapipeline {
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
- : SkiaPipeline(thread), mEglManager(thread.eglManager()) {}
+ : SkiaPipeline(thread), mEglManager(thread.eglManager()) {
+ thread.renderState().registerContextCallback(this);
+}
+
+SkiaOpenGLPipeline::~SkiaOpenGLPipeline() {
+ mRenderThread.renderState().removeContextCallback(this);
+}
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
// TODO: Figure out why this workaround is needed, see b/13913604
@@ -135,6 +142,13 @@
return new DeferredLayerUpdater(mRenderThread.renderState());
}
+void SkiaOpenGLPipeline::onContextDestroyed() {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+}
+
void SkiaOpenGLPipeline::onStop() {
if (mEglManager.isCurrent(mEglSurface)) {
mEglManager.makeCurrent(EGL_NO_SURFACE);
@@ -153,6 +167,12 @@
mEglSurface = mEglManager.createSurface(surface, colorMode);
}
+ if (colorMode == ColorMode::SRGB) {
+ mSurfaceColorType = SkColorType::kN32_SkColorType;
+ } else if (colorMode == ColorMode::WideColorGamut) {
+ mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+ }
+
if (mEglSurface != EGL_NO_SURFACE) {
const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
@@ -170,14 +190,6 @@
return CC_LIKELY(mEglManager.hasEglContext());
}
-SkColorType SkiaOpenGLPipeline::getSurfaceColorType() const {
- return mEglManager.getSurfaceColorType();
-}
-
-sk_sp<SkColorSpace> SkiaOpenGLPipeline::getSurfaceColorSpace() {
- return mEglManager.getSurfaceColorSpace();
-}
-
void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
if (thread.eglManager().hasEglContext()) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index fbdf313..4ab3541 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -18,6 +18,8 @@
#include "SkiaPipeline.h"
+#include "renderstate/RenderState.h"
+
namespace android {
class Bitmap;
@@ -25,10 +27,10 @@
namespace uirenderer {
namespace skiapipeline {
-class SkiaOpenGLPipeline : public SkiaPipeline {
+class SkiaOpenGLPipeline : public SkiaPipeline, public IGpuContextCallback {
public:
SkiaOpenGLPipeline(renderthread::RenderThread& thread);
- virtual ~SkiaOpenGLPipeline() {}
+ virtual ~SkiaOpenGLPipeline();
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
@@ -45,11 +47,12 @@
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
- SkColorType getSurfaceColorType() const override;
- sk_sp<SkColorSpace> getSurfaceColorSpace() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+protected:
+ void onContextDestroyed() override;
+
private:
renderthread::EglManager& mEglManager;
EGLSurface mEglSurface = EGL_NO_SURFACE;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 7f8abb8..2dfe7c7 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -200,10 +200,6 @@
return false;
}
-void SkiaPipeline::destroyLayer(RenderNode* node) {
- node->setLayerSurface(nullptr);
-}
-
void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
GrContext* context = thread.getGrContext();
if (context) {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index b78dea1..42a411a 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -48,14 +48,15 @@
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
ErrorHandler* errorHandler) override;
+ SkColorType getSurfaceColorType() const { return mSurfaceColorType; }
+ sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
+
void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, sk_sp<SkSurface> surface);
std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
- static void destroyLayer(RenderNode* node);
-
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
@@ -108,6 +109,8 @@
void dumpResourceCacheUsage() const;
renderthread::RenderThread& mRenderThread;
+ SkColorType mSurfaceColorType;
+ sk_sp<SkColorSpace> mSurfaceColorSpace;
private:
void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 3c281e78..fac07d7 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -113,8 +113,6 @@
// use staging property, since recording on UI thread
if (renderNode->stagingProperties().isProjectionReceiver()) {
mDisplayList->mProjectionReceiver = &renderNodeDrawable;
- // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
- mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
}
}
@@ -132,25 +130,8 @@
drawDrawable(functorDrawable);
}
-class VectorDrawable : public SkDrawable {
-public:
- VectorDrawable(VectorDrawableRoot* tree)
- : mRoot(tree)
- , mBounds(tree->stagingProperties()->getBounds()) {}
-
-protected:
- virtual SkRect onGetBounds() override { return mBounds; }
- virtual void onDraw(SkCanvas* canvas) override {
- mRoot->draw(canvas, mBounds);
- }
-
-private:
- sp<VectorDrawableRoot> mRoot;
- SkRect mBounds;
-};
-
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
- drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
+ mRecorder.drawVectorDrawable(tree);
mDisplayList->mVectorDrawables.push_back(tree);
}
@@ -196,7 +177,7 @@
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)));
+ mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), 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.
@@ -211,7 +192,7 @@
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
+ mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
@@ -226,7 +207,7 @@
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
- SkCanvas::kFast_SrcRectConstraint);
+ SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index e34f160..a2d8119 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -123,8 +123,7 @@
}
if (surface) {
- // TODO: handle color mode
- mVkSurface = mVkManager.createSurface(surface);
+ mVkSurface = mVkManager.createSurface(surface, colorMode);
}
return mVkSurface != nullptr;
@@ -138,14 +137,6 @@
return CC_LIKELY(mVkManager.hasVkContext());
}
-SkColorType SkiaVulkanPipeline::getSurfaceColorType() const {
- return mVkManager.getSurfaceColorType();
-}
-
-sk_sp<SkColorSpace> SkiaVulkanPipeline::getSurfaceColorSpace() {
- return mVkManager.getSurfaceColorSpace();
-}
-
void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
VkFunctorDrawable::vkInvokeFunctor(functor);
}
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 6e723a8..14c0d69 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -43,8 +43,6 @@
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
- SkColorType getSurfaceColorType() const override;
- sk_sp<SkColorSpace> getSurfaceColorSpace() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index b524bcb..fad9440 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -14,91 +14,28 @@
* limitations under the License.
*/
#include "renderstate/RenderState.h"
-#include <GpuMemoryTracker.h>
-#include "DeferredLayerUpdater.h"
-#include "Snapshot.h"
-#include "renderthread/CanvasContext.h"
-#include "renderthread/EglManager.h"
-#include "utils/GLUtils.h"
-
-#include <algorithm>
-
-#include <ui/ColorSpace.h>
+#include "renderthread/RenderThread.h"
+#include "GpuMemoryTracker.h"
namespace android {
namespace uirenderer {
-RenderState::RenderState(renderthread::RenderThread& thread)
- : mRenderThread(thread), mViewportWidth(0), mViewportHeight(0), mFramebuffer(0) {
+RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thread) {
mThreadId = pthread_self();
}
-RenderState::~RenderState() {
-}
-
void RenderState::onContextCreated() {
GpuMemoryTracker::onGpuContextCreated();
}
void RenderState::onContextDestroyed() {
- destroyLayersInUpdater();
+ for(auto callback : mContextCallbacks) {
+ callback->onContextDestroyed();
+ }
GpuMemoryTracker::onGpuContextDestroyed();
}
-GrContext* RenderState::getGrContext() const {
- return mRenderThread.getGrContext();
-}
-
-void RenderState::onBitmapDestroyed(uint32_t pixelRefId) {
- // DEAD CODE
-}
-
-void RenderState::setViewport(GLsizei width, GLsizei height) {
- mViewportWidth = width;
- mViewportHeight = height;
- glViewport(0, 0, mViewportWidth, mViewportHeight);
-}
-
-void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
- *outWidth = mViewportWidth;
- *outHeight = mViewportHeight;
-}
-
-void RenderState::bindFramebuffer(GLuint fbo) {
- if (mFramebuffer != fbo) {
- mFramebuffer = fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- }
-}
-
-GLuint RenderState::createFramebuffer() {
- GLuint ret;
- glGenFramebuffers(1, &ret);
- return ret;
-}
-
-void RenderState::deleteFramebuffer(GLuint fbo) {
- if (mFramebuffer == fbo) {
- // GL defines that deleting the currently bound FBO rebinds FBO 0.
- // Reflect this in our cached value.
- mFramebuffer = 0;
- }
- glDeleteFramebuffers(1, &fbo);
-}
-
-void RenderState::debugOverdraw(bool enable, bool clear) {
- // DEAD CODE
-}
-
-static void destroyLayerInUpdater(DeferredLayerUpdater* layerUpdater) {
- layerUpdater->destroyLayer();
-}
-
-void RenderState::destroyLayersInUpdater() {
- std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater);
-}
-
void RenderState::postDecStrong(VirtualLightRefBase* object) {
if (pthread_equal(mThreadId, pthread_self())) {
object->decStrong(nullptr);
@@ -111,13 +48,5 @@
// Render
///////////////////////////////////////////////////////////////////////////////
-void RenderState::dump() {
- // DEAD CODE
-}
-
-renderthread::RenderThread& RenderState::getRenderThread() {
- return mRenderThread;
-}
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index f39aa4b..ff5d02f 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -18,29 +18,26 @@
#include "utils/Macros.h"
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <private/hwui/DrawGlInfo.h>
-#include <ui/Region.h>
-#include <utils/Functor.h>
-#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <set>
-class GrContext;
-
namespace android {
namespace uirenderer {
class Layer;
-class DeferredLayerUpdater;
namespace renderthread {
class CacheManager;
-class CanvasContext;
class RenderThread;
}
+class IGpuContextCallback {
+public:
+ virtual void onContextDestroyed() = 0;
+protected:
+ virtual ~IGpuContextCallback() {}
+};
+
// wrapper of Caches for users to migrate to.
class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
@@ -48,66 +45,30 @@
friend class renderthread::CacheManager;
public:
- void onContextCreated();
- void onContextDestroyed();
-
- void onBitmapDestroyed(uint32_t pixelRefId);
-
- void setViewport(GLsizei width, GLsizei height);
- void getViewport(GLsizei* outWidth, GLsizei* outHeight);
-
- void bindFramebuffer(GLuint fbo);
- GLuint getFramebuffer() { return mFramebuffer; }
- GLuint createFramebuffer();
- void deleteFramebuffer(GLuint fbo);
-
- void debugOverdraw(bool enable, bool clear);
+ void registerContextCallback(IGpuContextCallback* cb) { mContextCallbacks.insert(cb); }
+ void removeContextCallback(IGpuContextCallback* cb) { mContextCallbacks.erase(cb); }
void registerLayer(Layer* layer) { mActiveLayers.insert(layer); }
void unregisterLayer(Layer* layer) { mActiveLayers.erase(layer); }
- void registerCanvasContext(renderthread::CanvasContext* context) {
- mRegisteredContexts.insert(context);
- }
-
- void unregisterCanvasContext(renderthread::CanvasContext* context) {
- mRegisteredContexts.erase(context);
- }
-
- void registerDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) {
- mActiveLayerUpdaters.insert(layerUpdater);
- }
-
- void unregisterDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) {
- mActiveLayerUpdaters.erase(layerUpdater);
- }
-
// TODO: This system is a little clunky feeling, this could use some
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
- GrContext* getGrContext() const;
-
- void dump();
-
- renderthread::RenderThread& getRenderThread();
+ renderthread::RenderThread& getRenderThread() const { return mRenderThread; }
private:
- void destroyLayersInUpdater();
-
explicit RenderState(renderthread::RenderThread& thread);
- ~RenderState();
+ ~RenderState() {}
+
+ // Context notifications are only to be triggered by renderthread::RenderThread
+ void onContextCreated();
+ void onContextDestroyed();
+
+ std::set<IGpuContextCallback*> mContextCallbacks;
+ std::set<Layer*> mActiveLayers;
renderthread::RenderThread& mRenderThread;
-
- std::set<Layer*> mActiveLayers;
- std::set<DeferredLayerUpdater*> mActiveLayerUpdaters;
- std::set<renderthread::CanvasContext*> mRegisteredContexts;
-
- GLsizei mViewportWidth;
- GLsizei mViewportHeight;
- GLuint mFramebuffer;
-
pthread_t mThreadId;
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ea6a851..92a749f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -27,7 +27,6 @@
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
-#include "renderstate/RenderState.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
#include "../Properties.h"
@@ -76,10 +75,6 @@
return nullptr;
}
-void CanvasContext::destroyLayer(RenderNode* node) {
- skiapipeline::SkiaPipeline::destroyLayer(node);
-}
-
void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) {
ATRACE_CALL();
auto renderType = Properties::getRenderPipelineType();
@@ -113,13 +108,11 @@
, mRenderPipeline(std::move(renderPipeline)) {
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
- mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
CanvasContext::~CanvasContext() {
destroy();
- mRenderThread.renderState().unregisterCanvasContext(this);
for (auto& node : mRenderNodes) {
node->clearRoot();
}
@@ -151,8 +144,7 @@
mNativeSurface = std::move(surface);
- // TODO(b/111436479) Introduce a way for app to specify DisplayColorGamut mode.
- ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Legacy;
+ ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
mFrameNumber = -1;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8448788..2307ee4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -96,12 +96,6 @@
*/
void unpinImages() { mRenderPipeline->unpinImages(); }
- /**
- * Destroy any layers that have been attached to the provided RenderNode removing
- * any state that may have been set during createOrUpdateLayer().
- */
- static void destroyLayer(RenderNode* node);
-
static void invokeFunctor(const RenderThread& thread, Functor* functor);
static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
@@ -188,6 +182,23 @@
mFrameCompleteCallbacks.push_back(std::move(func));
}
+ void setForceDark(bool enable) {
+ mUseForceDark = enable;
+ }
+
+ bool useForceDark() {
+ // The force-dark override has the highest priority, followed by the disable setting
+ // for the feature as a whole, followed last by whether or not this context has had
+ // force dark set (typically automatically done via UIMode)
+ if (Properties::forceDarkMode) {
+ return true;
+ }
+ if (!Properties::enableForceDarkSupport) {
+ return false;
+ }
+ return mUseForceDark;
+ }
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -234,6 +245,7 @@
bool mOpaque;
bool mWideColorGamut = false;
+ bool mUseForceDark = false;
LightInfo mLightInfo;
LightGeometry mLightGeometry = {{0, 0, 0}, 0};
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 0cb23e5..d4ffddd 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -126,17 +126,6 @@
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface, nullptr, /* force */ true);
-
- mSurfaceColorGamut = DataSpaceToColorGamut(
- static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace()));
-
- LOG_ALWAYS_FATAL_IF(mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut &&
- !EglExtensions.displayP3, "EGL doesn't support Display P3.");
-
- mSurfaceColorType = PixelFormatToColorType(
- static_cast<android_pixel_format>(DeviceInfo::get()->getTargetPixelFormat()));
- mSurfaceColorSpace = DataSpaceToColorSpace(
- static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace()));
}
void EglManager::initExtensions() {
@@ -309,21 +298,13 @@
if (wideColorGamut) {
attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
} else {
- if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) {
- attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT;
- } else {
- attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
- }
+ attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
}
#else
if (wideColorGamut) {
attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
} else {
- if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) {
- attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT;
- } else {
- attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
- }
+ attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
}
#endif
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index e97228c..55c81d4 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -78,9 +78,6 @@
// Depending on installed extensions, the result is either Android native fence or EGL fence.
status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence);
- SkColorType getSurfaceColorType() const { return mSurfaceColorType; }
- sk_sp<SkColorSpace> getSurfaceColorSpace() { return mSurfaceColorSpace; }
-
private:
void initExtensions();
@@ -95,9 +92,6 @@
EGLContext mEglContext;
EGLSurface mPBufferSurface;
EGLSurface mCurrentSurface;
- SkColorSpace::Gamut mSurfaceColorGamut;
- SkColorType mSurfaceColorType;
- sk_sp<SkColorSpace> mSurfaceColorSpace;
enum class SwapBehavior {
Discard,
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 0297c9c..4972554 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -43,15 +43,8 @@
enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded };
enum class ColorMode {
- // Legacy means HWUI will produce buffer with whatever platform prefers
- // HWUI to produce, however, HWUI doesn't accurately convert color from
- // source color space to destination color space, instead HWUI will take
- // the pixel value directly and interpret it destination color space.
- Legacy,
- // DisplayColorGamut means HWUI will produce buffer with whatever platform
- // prefers HWUI to produce and accurately convert color from source color
- // space to destination color space.
- DisplayColorGamut,
+ // SRGB means HWUI will produce buffer in SRGB color space.
+ SRGB,
// WideColorGamut means HWUI would support rendering scRGB non-linear into
// a signed buffer with enough range to support the wide color gamut of the
// display.
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 7a5348a..54219b5 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -298,6 +298,12 @@
});
}
+void RenderProxy::setForceDark(bool enable) {
+ mRenderThread.queue().post([this, enable]() {
+ mContext->setForceDark(enable);
+ });
+}
+
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
@@ -345,13 +351,6 @@
}
}
-void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) {
- if (!RenderThread::hasInstance()) return;
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post(
- [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); });
-}
-
void RenderProxy::disableVsync() {
Properties::disableVsync = true;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 969ad00..d29fcc4 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -119,7 +119,7 @@
ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
- ANDROID_API long getDroppedFrameReportCount();
+ ANDROID_API void setForceDark(bool enable);
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
int bottom, SkBitmap* bitmap);
@@ -127,8 +127,6 @@
static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
- static void onBitmapDestroyed(uint32_t pixelRefId);
-
ANDROID_API static void disableVsync();
static void repackVectorDrawableAtlas();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a5dcc72..207673c1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -177,7 +177,6 @@
return;
}
mEglManager->initialize();
- renderState().onContextCreated();
#ifdef HWUI_GLES_WRAP_ENABLED
debug::GlesDriver* driver = debug::GlesDriver::get();
@@ -201,7 +200,6 @@
void RenderThread::destroyGlContext() {
if (mEglManager->hasEglContext()) {
setGrContext(nullptr);
- renderState().onContextDestroyed();
mEglManager->destroy();
}
}
@@ -243,10 +241,12 @@
void RenderThread::setGrContext(sk_sp<GrContext> context) {
mCacheManager->reset(context);
if (mGrContext) {
+ mRenderState->onContextDestroyed();
mGrContext->releaseResourcesAndAbandonContext();
}
mGrContext = std::move(context);
if (mGrContext) {
+ mRenderState->onContextCreated();
DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
}
}
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4881172..83e9db3 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,6 +16,8 @@
#include "VulkanManager.h"
+#include <private/gui/SyncFeatures.h>
+
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
@@ -39,9 +41,12 @@
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- mRenderThread.renderState().onContextDestroyed();
mRenderThread.setGrContext(nullptr);
+ // We don't need to explicitly free the command buffer since it automatically gets freed when we
+ // delete the VkCommandPool below.
+ mDummyCB = VK_NULL_HANDLE;
+
if (VK_NULL_HANDLE != mCommandPool) {
mDestroyCommandPool(mDevice, mCommandPool, nullptr);
mCommandPool = VK_NULL_HANDLE;
@@ -227,6 +232,11 @@
grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+ if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ this->destroy();
+ return false;
+ }
+
memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2));
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features.pNext = nullptr;
@@ -314,6 +324,8 @@
GET_DEV_PROC(DeviceWaitIdle);
GET_DEV_PROC(CreateSemaphore);
GET_DEV_PROC(DestroySemaphore);
+ GET_DEV_PROC(ImportSemaphoreFdKHR);
+ GET_DEV_PROC(GetSemaphoreFdKHR);
GET_DEV_PROC(CreateFence);
GET_DEV_PROC(DestroyFence);
GET_DEV_PROC(WaitForFences);
@@ -385,6 +397,14 @@
&mCommandPool);
SkASSERT(VK_SUCCESS == res);
}
+ LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE);
+
+ if (!setupDummyCommandBuffer()) {
+ this->destroy();
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
@@ -401,8 +421,6 @@
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
-
- mRenderThread.renderState().onContextCreated();
}
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -600,7 +618,8 @@
VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
- kRGBA_8888_SkColorType, nullptr, &props);
+ surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
+ : kRGBA_8888_SkColorType, nullptr, &props);
}
SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -715,24 +734,22 @@
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
- // Pick our surface format. For now, just make sure it matches our sRGB request:
- VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
+ VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM;
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-
- bool wantSRGB = false;
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- wantSRGB = true;
-#endif
+ if (surface->mColorMode == ColorMode::WideColorGamut) {
+ surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
+ colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
+ }
+ bool foundSurfaceFormat = false;
for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
- // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB
- VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
- if (desiredFormat == surfaceFormats[i].format) {
- surfaceFormat = surfaceFormats[i].format;
- colorSpace = surfaceFormats[i].colorSpace;
+ if (surfaceFormat == surfaceFormats[i].format
+ && colorSpace == surfaceFormats[i].colorSpace) {
+ foundSurfaceFormat = true;
+ break;
}
}
- if (VK_FORMAT_UNDEFINED == surfaceFormat) {
+ if (!foundSurfaceFormat) {
return false;
}
@@ -794,14 +811,14 @@
return true;
}
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) {
initialize();
if (!window) {
return nullptr;
}
- VulkanSurface* surface = new VulkanSurface();
+ VulkanSurface* surface = new VulkanSurface(colorMode);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
@@ -979,19 +996,176 @@
return surface->mCurrentTime - lastUsed;
}
+bool VulkanManager::setupDummyCommandBuffer() {
+ if (mDummyCB != VK_NULL_HANDLE) {
+ return true;
+ }
+
+ VkCommandBufferAllocateInfo commandBuffersInfo;
+ memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
+ commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ commandBuffersInfo.pNext = nullptr;
+ commandBuffersInfo.commandPool = mCommandPool;
+ commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ commandBuffersInfo.commandBufferCount = 1;
+
+ VkResult err = mAllocateCommandBuffers(mDevice, &commandBuffersInfo, &mDummyCB);
+ if (err != VK_SUCCESS) {
+ // It is probably unnecessary to set this back to VK_NULL_HANDLE, but we set it anyways to
+ // make sure the driver didn't set a value and then return a failure.
+ mDummyCB = VK_NULL_HANDLE;
+ return false;
+ }
+
+ VkCommandBufferBeginInfo beginInfo;
+ memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo));
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
+
+ mBeginCommandBuffer(mDummyCB, &beginInfo);
+ mEndCommandBuffer(mDummyCB);
+ return true;
+}
+
status_t VulkanManager::fenceWait(sp<Fence>& fence) {
- //TODO: Insert a wait on fence command into the Vulkan command buffer.
- // Block CPU on the fence.
- status_t err = fence->waitForever("VulkanManager::fenceWait");
- if (err != NO_ERROR) {
- ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err);
- return err;
+ if (!hasVkContext()) {
+ ALOGE("VulkanManager::fenceWait: VkDevice not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Block GPU on the fence.
+ int fenceFd = fence->dup();
+ if (fenceFd == -1) {
+ ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("Failed to create import semaphore, err: %d", err);
+ return UNKNOWN_ERROR;
+ }
+ VkImportSemaphoreFdInfoKHR importInfo;
+ importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+ importInfo.pNext = nullptr;
+ importInfo.semaphore = semaphore;
+ importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+ importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ importInfo.fd = fenceFd;
+
+ err = mImportSemaphoreFdKHR(mDevice, &importInfo);
+ if (VK_SUCCESS != err) {
+ ALOGE("Failed to import semaphore, err: %d", err);
+ return UNKNOWN_ERROR;
+ }
+
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+ VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 1;
+ // Wait to make sure aquire semaphore set above has signaled.
+ submitInfo.pWaitSemaphores = &semaphore;
+ submitInfo.pWaitDstStageMask = &waitDstStageFlags;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &mDummyCB;
+ submitInfo.signalSemaphoreCount = 0;
+
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+ // On Android when we import a semaphore, it is imported using temporary permanence. That
+ // means as soon as we queue the semaphore for a wait it reverts to its previous permanent
+ // state before importing. This means it will now be in an idle state with no pending
+ // signal or wait operations, so it is safe to immediately delete it.
+ mDestroySemaphore(mDevice, semaphore, nullptr);
+ } else {
+ // Block CPU on the fence.
+ status_t err = fence->waitForever("VulkanManager::fenceWait");
+ if (err != NO_ERROR) {
+ ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err);
+ return err;
+ }
}
return OK;
}
status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) {
- //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed.
+ if (!hasVkContext()) {
+ ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useFenceSync()) {
+ ALOGE("VulkanManager::createReleaseFence: Vk backend doesn't support non-native fences");
+ return INVALID_OPERATION;
+ }
+
+ if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+ return OK;
+ }
+
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("VulkanManager::createReleaseFence: Failed to create semaphore");
+ return INVALID_OPERATION;
+ }
+
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitSemaphores = nullptr;
+ submitInfo.pWaitDstStageMask = nullptr;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &mDummyCB;
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = &semaphore;
+
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ int fenceFd = 0;
+
+ err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ if (VK_SUCCESS != err) {
+ ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd");
+ return INVALID_OPERATION;
+ }
+ nativeFence = new Fence(fenceFd);
+
+ // Exporting a semaphore with copy transference via vkGetSemahporeFdKHR, has the same effect of
+ // destroying the semaphore and creating a new one with the same handle, and the payloads
+ // ownership is move to the Fd we created. Thus the semahpore is in a state that we can delete
+ // it and we don't need to wait on the command buffer we submitted to finish.
+ mDestroySemaphore(mDevice, semaphore, nullptr);
+
return OK;
}
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 7a539ae..7c59b6d 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -26,6 +26,7 @@
#include <ui/Fence.h>
#include <utils/StrongPointer.h>
#include <vk/GrVkBackendContext.h>
+#include "IRenderPipeline.h"
class GrVkExtensions;
@@ -37,7 +38,7 @@
class VulkanSurface {
public:
- VulkanSurface() {}
+ VulkanSurface(ColorMode colorMode) : mColorMode(colorMode) {}
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
@@ -73,6 +74,7 @@
VkImage* mImages = nullptr;
ImageInfo* mImageInfos;
uint16_t mCurrentTime = 0;
+ ColorMode mColorMode;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -90,7 +92,7 @@
// 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);
+ VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode);
// Destroy the VulkanSurface and all associated vulkan objects.
void destroySurface(VulkanSurface* surface);
@@ -118,10 +120,6 @@
// Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
status_t createReleaseFence(sp<Fence>& nativeFence);
- // TODO(b/115636873): Handle composition preference.
- SkColorType getSurfaceColorType() const { return SkColorType::kN32_SkColorType; }
- sk_sp<SkColorSpace> getSurfaceColorSpace() { return SkColorSpace::MakeSRGB(); }
-
private:
friend class RenderThread;
@@ -139,6 +137,8 @@
VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface);
+ bool setupDummyCommandBuffer();
+
// simple wrapper class that exists only to initialize a pointer to NULL
template <typename FNPTR_TYPE>
class VkPtr {
@@ -199,6 +199,8 @@
VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
+ VkPtr<PFN_vkImportSemaphoreFdKHR> mImportSemaphoreFdKHR;
+ VkPtr<PFN_vkGetSemaphoreFdKHR> mGetSemaphoreFdKHR;
VkPtr<PFN_vkCreateFence> mCreateFence;
VkPtr<PFN_vkDestroyFence> mDestroyFence;
VkPtr<PFN_vkWaitForFences> mWaitForFences;
@@ -216,6 +218,8 @@
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
+ VkCommandBuffer mDummyCB = VK_NULL_HANDLE;
+
enum class SwapBehavior {
Discard,
BufferAge,
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index c35f512..a00b8db 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -24,6 +24,7 @@
#include <Snapshot.h>
#include <hwui/Bitmap.h>
#include <pipeline/skia/SkiaRecordingCanvas.h>
+#include <private/hwui/DrawGlInfo.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 5e5d134..cdf31da 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -382,3 +382,13 @@
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ EXPECT_FALSE(pipeline->isSurfaceReady());
+ EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
+ EXPECT_TRUE(pipeline->isSurfaceReady());
+ renderThread.destroyGlContext();
+ EXPECT_FALSE(pipeline->isSurfaceReady());
+}
+
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 9f71e91..3fb6a31 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -57,21 +57,6 @@
return false;
}
-SkColorType PixelFormatToColorType(android_pixel_format pixelFormat) {
- switch (pixelFormat) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- return SkColorType::kN32_SkColorType;
- case HAL_PIXEL_FORMAT_RGBA_FP16:
- return SkColorType::kRGBA_F16_SkColorType;
- case HAL_PIXEL_FORMAT_RGBA_1010102:
- return SkColorType::kRGBA_1010102_SkColorType;
- default:
- ALOGW("Unsupported pixel format: %d, return kN32 by default", pixelFormat);
- return SkColorType::kN32_SkColorType;
- }
-}
-
android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
switch (colorType) {
case kRGBA_8888_SkColorType:
@@ -92,30 +77,6 @@
}
}
-SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) {
- switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) {
- case HAL_DATASPACE_STANDARD_BT709:
- return SkColorSpace::kSRGB_Gamut;
- case HAL_DATASPACE_STANDARD_BT2020:
- return SkColorSpace::kRec2020_Gamut;
- case HAL_DATASPACE_STANDARD_DCI_P3:
- return SkColorSpace::kDCIP3_D65_Gamut;
- case HAL_DATASPACE_STANDARD_ADOBE_RGB:
- return SkColorSpace::kAdobeRGB_Gamut;
- case HAL_DATASPACE_STANDARD_UNSPECIFIED:
- case HAL_DATASPACE_STANDARD_BT601_625:
- case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
- case HAL_DATASPACE_STANDARD_BT601_525:
- case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
- case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
- case HAL_DATASPACE_STANDARD_BT470M:
- case HAL_DATASPACE_STANDARD_FILM:
- default:
- ALOGW("Unsupported Gamut: %d, return SRGB gamut by default", dataSpace);
- return SkColorSpace::kSRGB_Gamut;
- }
-}
-
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
SkColorSpace::Gamut gamut;
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index e935a0d..4daccda 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -115,12 +115,8 @@
// returns true for sRGB, gamma 2.2 and Display P3 for instance
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
-SkColorType PixelFormatToColorType(android_pixel_format pixelFormat);
-
android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
-SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace);
-
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
struct Lab {
diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java
index 335ad5e..83f05c2 100644
--- a/location/java/android/location/Address.java
+++ b/location/java/android/location/Address.java
@@ -363,7 +363,7 @@
* or null if it is unknown.
*
* @throws IllegalStateException if this Address has not been assigned
- * a latitude.
+ * a phone number.
*/
public String getPhone() {
return mPhone;
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index 447195d..b09335c 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -18,4 +18,5 @@
name: "com.android.location.provider",
srcs: ["java/**/*.java"],
api_packages: ["com.android.location.provider"],
+ metalava_enabled: false,
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ed4da22..340f279 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -336,7 +336,7 @@
*
* <table border="0" cellspacing="0" cellpadding="0">
* <tr><td>Method Name </p></td>
- * <td>Valid Sates </p></td>
+ * <td>Valid States </p></td>
* <td>Invalid States </p></td>
* <td>Comments </p></td></tr>
* <tr><td>attachAuxEffect </p></td>
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6187900..dfcbabe 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -36,8 +36,6 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
@@ -60,7 +58,6 @@
import dalvik.system.CloseGuard;
import libcore.io.IoBridge;
-import libcore.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -86,6 +83,7 @@
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -253,21 +251,6 @@
void process() {
stayAwake(false);
- // TODO: remove this block when native code allows prepared -> pause
- // and sends MEDIA_INFO_DATA_SOURCE_START when pipeline is created.
- if (getState() == PLAYER_STATE_PREPARED) {
- final DataSourceDesc dsd;
- synchronized (mSrcLock) {
- dsd = mCurrentDSD;
- }
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- MediaPlayer2Impl.this, dsd, MEDIA_INFO_DATA_SOURCE_START, 0));
- }
- }
- }
-
_pause();
}
});
@@ -285,7 +268,10 @@
addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
@Override
void process() {
- // TODO: switch to next data source and play
+ if (getState() == PLAYER_STATE_PLAYING) {
+ pause();
+ }
+ playNextDataSource();
}
});
}
@@ -346,19 +332,14 @@
final String msg = "Cannot set AudioAttributes to null";
throw new IllegalArgumentException(msg);
}
- Parcel pattributes = Parcel.obtain();
- attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
- setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
- pattributes.recycle();
+ setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, attributes);
}
});
}
@Override
public @NonNull AudioAttributes getAudioAttributes() {
- Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
- AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes);
- pattributes.recycle();
+ AudioAttributes attributes = (AudioAttributes) getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
return attributes;
}
@@ -572,12 +553,13 @@
addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
@Override
void process() {
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onCommandLabelReached(
- MediaPlayer2Impl.this, label));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onCommandLabelReached(
+ MediaPlayer2Impl.this, label);
}
- }
+ });
}
});
}
@@ -995,12 +977,13 @@
}
if (!hasNextDSD) {
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- MediaPlayer2Impl.this, null, MEDIA_INFO_DATA_SOURCE_LIST_END, 0));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onInfo(
+ MediaPlayer2Impl.this, null, MEDIA_INFO_DATA_SOURCE_LIST_END, 0);
}
- }
+ });
}
}
@@ -1597,9 +1580,9 @@
* @param value value of the parameter to be set.
* @return true if the parameter is set successfully, false otherwise
*/
- private native boolean setParameter(int key, Parcel value);
+ private native boolean setParameter(int key, Object value);
- private native Parcel getParameter(int key);
+ private native Object getParameter(int key);
/**
@@ -2583,12 +2566,13 @@
}
if (dsd != null) {
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0);
}
- }
+ });
}
synchronized (mSrcLock) {
@@ -2636,12 +2620,13 @@
// notifying the client outside the lock
if (drmInfo != null) {
- synchronized (mEventCbLock) {
- for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onDrmInfo(
- mMediaPlayer, dsd, drmInfo));
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmInfo(
+ mMediaPlayer, dsd, drmInfo);
}
- }
+ });
}
} else {
Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
@@ -2652,12 +2637,13 @@
case MEDIA_PLAYBACK_COMPLETE:
{
if (isCurrentSrcId) {
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0);
}
- }
+ });
stayAwake(false);
synchronized (mSrcLock) {
@@ -2695,13 +2681,13 @@
case MEDIA_BUFFERING_UPDATE:
{
final int percent = msg.arg1;
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE,
- percent));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE, percent);
}
- }
+ });
synchronized (mSrcLock) {
if (isCurrentSrcId) {
@@ -2740,26 +2726,33 @@
{
final int width = msg.arg1;
final int height = msg.arg2;
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onVideoSizeChanged(
- mMediaPlayer, dsd, width, height));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onVideoSizeChanged(
+ mMediaPlayer, dsd, width, height);
}
- }
+ });
return;
}
case MEDIA_ERROR:
{
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onError(
- mMediaPlayer, dsd, what, extra));
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onError(
+ mMediaPlayer, dsd, what, extra);
}
- }
+ });
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0);
+ }
+ });
stayAwake(false);
return;
}
@@ -2799,12 +2792,13 @@
break;
}
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, what, extra));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onInfo(
+ mMediaPlayer, dsd, what, extra);
}
- }
+ });
if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
if (isCurrentSrcId) {
@@ -2841,12 +2835,13 @@
text = null;
}
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onTimedText(
- mMediaPlayer, dsd, text));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onTimedText(
+ mMediaPlayer, dsd, text);
}
- }
+ });
return;
}
@@ -2866,12 +2861,13 @@
in.next().getInt64Value(), // startTimeUs
in.next().getInt64Value(), // durationUs
in.next().getBytesValue().toByteArray()); // data
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onSubtitleData(
- mMediaPlayer, dsd, data));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onSubtitleData(
+ mMediaPlayer, dsd, data);
}
- }
+ });
}
return;
}
@@ -2895,12 +2891,13 @@
data = null;
}
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
- mMediaPlayer, dsd, data));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onTimedMetaDataAvailable(
+ mMediaPlayer, dsd, data);
}
- }
+ });
return;
}
@@ -3033,6 +3030,40 @@
}
}
+ private void sendEvent(final EventNotifier notifier) {
+ synchronized (mEventCbLock) {
+ try {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> notifier.notify(cb.second));
+ }
+ } catch (RejectedExecutionException e) {
+ // The executor has been shut down.
+ Log.w(TAG, "The executor has been shut down. Ignoring event.");
+ }
+ }
+ }
+
+ private void sendDrmEvent(final DrmEventNotifier notifier) {
+ synchronized (mDrmEventCbLock) {
+ try {
+ for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+ cb.first.execute(() -> notifier.notify(cb.second));
+ }
+ } catch (RejectedExecutionException e) {
+ // The executor has been shut down.
+ Log.w(TAG, "The executor has been shut down. Ignoring drm event.");
+ }
+ }
+ }
+
+ private interface EventNotifier {
+ void notify(EventCallback callback);
+ }
+
+ private interface DrmEventNotifier {
+ void notify(DrmEventCallback callback);
+ }
+
// Modular DRM begin
/**
@@ -3283,12 +3314,13 @@
// if finished successfully without provisioning, call the callback outside the lock
if (allDoneWithoutProvisioning) {
- synchronized (mDrmEventCbLock) {
- for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onDrmPrepared(
- this, mCurrentDSD, PREPARE_DRM_STATUS_SUCCESS));
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmPrepared(
+ MediaPlayer2Impl.this, mCurrentDSD, PREPARE_DRM_STATUS_SUCCESS);
}
- }
+ });
}
}
@@ -3649,7 +3681,7 @@
supportedSchemes[i]);
}
- Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + pssh.length +
+ Log.v(TAG, "DrmInfoImpl() psshsize: " + pssh.length +
" supportedDRMsCount: " + supportedDRMsCount);
}
@@ -3914,7 +3946,7 @@
connection.setReadTimeout(TIMEOUT_MS);
connection.connect();
- response = Streams.readFully(connection.getInputStream());
+ response = readInputStreamFully(connection.getInputStream());
Log.v(TAG, "HandleProvisioninig: Thread run: response " +
response.length + " " + response);
@@ -3968,12 +4000,13 @@
} // synchronized
// calling the callback outside the lock
- synchronized (mDrmEventCbLock) {
- for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onDrmPrepared(
- mediaPlayer, mCurrentDSD, status));
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmPrepared(
+ mediaPlayer, mCurrentDSD, status);
}
- }
+ });
} else { // blocking mode already has the lock
// continuing with prepareDrm
@@ -3993,6 +4026,29 @@
finished = true;
} // run()
+ /**
+ * 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(UUID uuid) {
@@ -4632,12 +4688,13 @@
if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
return;
}
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onCallCompleted(
- MediaPlayer2Impl.this, mDSD, mMediaCallType, status));
+ sendEvent(new EventNotifier() {
+ @Override
+ public void notify(EventCallback callback) {
+ callback.onCallCompleted(
+ MediaPlayer2Impl.this, mDSD, mMediaCallType, status);
}
- }
+ });
}
};
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0ff2d8f..c537945 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -160,8 +160,9 @@
public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
- private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
- private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
+ private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
+ private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
+ private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
private static String sLastInternalScanFingerprint;
private static final String[] ID3_GENRES = {
@@ -1193,6 +1194,9 @@
if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
|| path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
|| path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
+ || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
+ || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
+ || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
|| path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
|| path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
|| path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 1a844cc..693a3d0 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1490,8 +1490,8 @@
{"_release", "()V", (void *)android_media_MediaPlayer2_release},
{"_reset", "()V", (void *)android_media_MediaPlayer2_reset},
{"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer2_getAudioStreamType},
- {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer2_setParameter},
- {"getParameter", "(I)Landroid/os/Parcel;", (void *)android_media_MediaPlayer2_getParameter},
+ {"setParameter", "(ILjava/lang/Object;)Z", (void *)android_media_MediaPlayer2_setParameter},
+ {"getParameter", "(I)Ljava/lang/Object;", (void *)android_media_MediaPlayer2_getParameter},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
{"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume},
diff --git a/media/lib/remotedisplay/Android.bp b/media/lib/remotedisplay/Android.bp
index 1e9320d..5f4b930 100644
--- a/media/lib/remotedisplay/Android.bp
+++ b/media/lib/remotedisplay/Android.bp
@@ -14,22 +14,8 @@
// limitations under the License.
//
-droiddoc {
- name: "com.android.media.remotedisplay.stubs-gen-docs",
- srcs: [
- "java/**/*.java",
- ],
- args: " -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
- " -stubpackages com.android.media.remotedisplay " +
- " -nodocs ",
- custom_template: "droiddoc-templates-sdk",
- installable: false,
-}
-
-java_library_static {
- name: "com.android.media.remotedisplay.stubs",
- srcs: [
- ":com.android.media.remotedisplay.stubs-gen-docs",
- ],
- sdk_version: "current",
+java_sdk_library {
+ name: "com.android.media.remotedisplay",
+ srcs: ["java/**/*.java"],
+ api_packages: ["com.android.media.remotedisplay"],
}
diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk
deleted file mode 100644
index e88c0f1..0000000
--- a/media/lib/remotedisplay/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
-
-# the remotedisplay library
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= com.android.media.remotedisplay
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
-
-include $(BUILD_JAVA_LIBRARY)
-
-
-# ==== com.android.media.remotedisplay.xml lib def ========================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := com.android.media.remotedisplay.xml
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_CLASS := ETC
-
-# This will install the file in /system/etc/permissions
-#
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-
-include $(BUILD_PREBUILT)
diff --git a/media/lib/remotedisplay/OWNERS b/media/lib/remotedisplay/OWNERS
new file mode 100644
index 0000000..7e7335d
--- /dev/null
+++ b/media/lib/remotedisplay/OWNERS
@@ -0,0 +1 @@
+michaelwr@google.com
diff --git a/media/lib/remotedisplay/api/current.txt b/media/lib/remotedisplay/api/current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/lib/remotedisplay/api/current.txt
diff --git a/media/lib/remotedisplay/api/removed.txt b/media/lib/remotedisplay/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/lib/remotedisplay/api/removed.txt
diff --git a/media/lib/remotedisplay/api/system-current.txt b/media/lib/remotedisplay/api/system-current.txt
new file mode 100644
index 0000000..69bbd35
--- /dev/null
+++ b/media/lib/remotedisplay/api/system-current.txt
@@ -0,0 +1,52 @@
+package com.android.media.remotedisplay {
+
+ public class RemoteDisplay {
+ ctor public RemoteDisplay(java.lang.String, java.lang.String);
+ method public java.lang.String getDescription();
+ method public java.lang.String getId();
+ method public java.lang.String getName();
+ method public int getPresentationDisplayId();
+ method public int getStatus();
+ method public int getVolume();
+ method public int getVolumeHandling();
+ method public int getVolumeMax();
+ method public void setDescription(java.lang.String);
+ method public void setName(java.lang.String);
+ method public void setPresentationDisplayId(int);
+ method public void setStatus(int);
+ method public void setVolume(int);
+ method public void setVolumeHandling(int);
+ method public void setVolumeMax(int);
+ field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
+ field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+ field public static final int STATUS_AVAILABLE = 2; // 0x2
+ field public static final int STATUS_CONNECTED = 4; // 0x4
+ field public static final int STATUS_CONNECTING = 3; // 0x3
+ field public static final int STATUS_IN_USE = 1; // 0x1
+ field public static final int STATUS_NOT_AVAILABLE = 0; // 0x0
+ }
+
+ public abstract class RemoteDisplayProvider {
+ ctor public RemoteDisplayProvider(android.content.Context);
+ method public void addDisplay(com.android.media.remotedisplay.RemoteDisplay);
+ method public com.android.media.remotedisplay.RemoteDisplay findRemoteDisplay(java.lang.String);
+ method public android.os.IBinder getBinder();
+ method public final android.content.Context getContext();
+ method public int getDiscoveryMode();
+ method public java.util.Collection<com.android.media.remotedisplay.RemoteDisplay> getDisplays();
+ method public android.app.PendingIntent getSettingsPendingIntent();
+ method public void onAdjustVolume(com.android.media.remotedisplay.RemoteDisplay, int);
+ method public void onConnect(com.android.media.remotedisplay.RemoteDisplay);
+ method public void onDisconnect(com.android.media.remotedisplay.RemoteDisplay);
+ method public void onDiscoveryModeChanged(int);
+ method public void onSetVolume(com.android.media.remotedisplay.RemoteDisplay, int);
+ method public void removeDisplay(com.android.media.remotedisplay.RemoteDisplay);
+ method public void updateDisplay(com.android.media.remotedisplay.RemoteDisplay);
+ field public static final int DISCOVERY_MODE_ACTIVE = 2; // 0x2
+ field public static final int DISCOVERY_MODE_NONE = 0; // 0x0
+ field public static final int DISCOVERY_MODE_PASSIVE = 1; // 0x1
+ field public static final java.lang.String SERVICE_INTERFACE = "com.android.media.remotedisplay.RemoteDisplayProvider";
+ }
+
+}
+
diff --git a/media/lib/remotedisplay/api/system-removed.txt b/media/lib/remotedisplay/api/system-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/lib/remotedisplay/api/system-removed.txt
diff --git a/media/lib/remotedisplay/api/test-current.txt b/media/lib/remotedisplay/api/test-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/lib/remotedisplay/api/test-current.txt
diff --git a/media/lib/remotedisplay/api/test-removed.txt b/media/lib/remotedisplay/api/test-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/lib/remotedisplay/api/test-removed.txt
diff --git a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java
index dc9dd79..8de414b 100644
--- a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java
+++ b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java
@@ -16,6 +16,7 @@
package com.android.media.remotedisplay;
+import android.annotation.SystemApi;
import android.media.RemoteDisplayState.RemoteDisplayInfo;
import android.text.TextUtils;
@@ -23,7 +24,10 @@
/**
* Represents a remote display that has been discovered.
+ *
+ * @hide
*/
+@SystemApi
public class RemoteDisplay {
private final RemoteDisplayInfo mMutableInfo;
private RemoteDisplayInfo mImmutableInfo;
diff --git a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
index 4d3edb8..7017e44 100644
--- a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
+++ b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
@@ -16,6 +16,7 @@
package com.android.media.remotedisplay;
+import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
@@ -88,7 +89,10 @@
* IMPORTANT: This class is effectively a public API for unbundled applications, and
* must remain API stable. See README.txt in the root of this package for more information.
* </p>
+ *
+ * @hide
*/
+@SystemApi
public abstract class RemoteDisplayProvider {
private static final int MSG_SET_CALLBACK = 1;
private static final int MSG_SET_DISCOVERY_MODE = 2;
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 3b25787..8c43683 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -18,4 +18,5 @@
name: "com.android.mediadrm.signer",
srcs: ["java/**/*.java"],
api_packages: ["com.android.mediadrm.signer"],
+ metalava_enabled: false,
}
diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml
index e64fb76..d8f7be9 100644
--- a/packages/CaptivePortalLogin/res/values-de/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-de/strings.xml
@@ -7,6 +7,6 @@
<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 möglicherweise nicht zur angezeigten Organisation."</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>
</resources>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 149de13..0ba37ae 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -130,6 +130,22 @@
mProbeSpec = null;
}
+ mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onLost(Network lostNetwork) {
+ // If the network disappears while the app is up, exit.
+ if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
+ }
+ };
+ mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), mNetworkCallback);
+
+ // If the network has disappeared, exit.
+ final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
+ if (networkCapabilities == null) {
+ finishAndRemoveTask();
+ return;
+ }
+
// Also initializes proxy system properties.
mNetwork = mNetwork.getPrivateDnsBypassingCopy();
mCm.bindProcessToNetwork(mNetwork);
@@ -139,24 +155,6 @@
// setContentView initializes the WebView logic which in turn reads the system properties.
setContentView(R.layout.activity_captive_portal_login);
- // Exit app if Network disappears.
- final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
- if (networkCapabilities == null) {
- finishAndRemoveTask();
- return;
- }
- mNetworkCallback = new NetworkCallback() {
- @Override
- public void onLost(Network lostNetwork) {
- if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
- }
- };
- final NetworkRequest.Builder builder = new NetworkRequest.Builder();
- for (int transportType : networkCapabilities.getTransportTypes()) {
- builder.addTransportType(transportType);
- }
- mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
-
getActionBar().setDisplayShowHomeEnabled(false);
getActionBar().setElevation(0); // remove shadow
getActionBar().setTitle(getHeaderTitle());
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 86f9032..1b1bf8d 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -30,6 +30,7 @@
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
+ "android.car.user",
"androidx.car_car",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
@@ -51,7 +52,6 @@
libs: [
"telephony-common",
"android.car",
- "android.car.user",
],
manifest: "AndroidManifest.xml",
diff --git a/packages/CarrierDefaultApp/res/values-de/strings.xml b/packages/CarrierDefaultApp/res/values-de/strings.xml
index 927b8f0..95639ad 100644
--- a/packages/CarrierDefaultApp/res/values-de/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-de/strings.xml
@@ -12,6 +12,6 @@
<string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status der mobilen Datennutzung"</string>
<string name="action_bar_label" msgid="4290345990334377177">"Bei Mobilfunknetz anmelden"</string>
<string name="ssl_error_warning" msgid="3127935140338254180">"Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."</string>
- <string name="ssl_error_example" msgid="6188711843183058764">"Beispiel: Die Log-in-Seite gehört möglicherweise nicht zur angezeigten Organisation."</string>
+ <string name="ssl_error_example" msgid="6188711843183058764">"Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Trotzdem in einem Browser fortfahren"</string>
</resources>
diff --git a/packages/LocalTransport/Android.mk b/packages/LocalTransport/Android.mk
new file mode 100644
index 0000000..3484b0f
--- /dev/null
+++ b/packages/LocalTransport/Android.mk
@@ -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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PACKAGE_NAME := LocalTransport
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/LocalTransport/AndroidManifest.xml b/packages/LocalTransport/AndroidManifest.xml
new file mode 100644
index 0000000..196be1e
--- /dev/null
+++ b/packages/LocalTransport/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2018 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.localtransport"
+ android:sharedUserId="android.uid.system" >
+
+
+ <application android:allowBackup="false" >
+ <!-- This service does not need to be exported because it shares uid with the system server
+ which is the only client. -->
+ <service android:name=".LocalTransportService"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.backup.TRANSPORT_HOST" />
+ </intent-filter>
+ </service>
+
+ </application>
+</manifest>
diff --git a/packages/LocalTransport/proguard.flags b/packages/LocalTransport/proguard.flags
new file mode 100644
index 0000000..c1f51b8
--- /dev/null
+++ b/packages/LocalTransport/proguard.flags
@@ -0,0 +1,5 @@
+-keep class com.android.localTransport.LocalTransport
+-keep class com.android.localTransport.LocalTransportParameters
+-keep class com.android.localTransport.LocalTransportService
+
+
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
similarity index 97%
rename from core/java/com/android/internal/backup/LocalTransport.java
rename to packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index d0f0272..0bf8bc1 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.backup;
+package com.android.localtransport;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -26,7 +26,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
-import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
@@ -56,7 +55,7 @@
private static final boolean DEBUG = false;
private static final String TRANSPORT_DIR_NAME
- = "com.android.internal.backup.LocalTransport";
+ = "com.android.localtransport.LocalTransport";
private static final String TRANSPORT_DESTINATION_STRING
= "Backing up to debug-only private cache";
@@ -75,10 +74,10 @@
private static final long KEY_VALUE_BACKUP_SIZE_QUOTA = 5 * 1024 * 1024;
private Context mContext;
- private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
- private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
- private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
- private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
+ private File mDataDir;
+ private File mCurrentSetDir;
+ private File mCurrentSetIncrementalDir;
+ private File mCurrentSetFullDir;
private PackageInfo[] mRestorePackages = null;
private int mRestorePackage = -1; // Index into mRestorePackages
@@ -101,6 +100,11 @@
private final LocalTransportParameters mParameters;
private void makeDataDirs() {
+ mDataDir = mContext.getFilesDir();
+ mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
+ mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
+ mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
+
mCurrentSetDir.mkdirs();
mCurrentSetFullDir.mkdir();
mCurrentSetIncrementalDir.mkdir();
diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
similarity index 97%
rename from core/java/com/android/internal/backup/LocalTransportParameters.java
rename to packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index 2427d39..784be22 100644
--- a/core/java/com/android/internal/backup/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.internal.backup;
+package com.android.localtransport;
import android.util.KeyValueSettingObserver;
import android.content.ContentResolver;
diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportService.java
similarity index 96%
rename from core/java/com/android/internal/backup/LocalTransportService.java
rename to packages/LocalTransport/src/com/android/localtransport/LocalTransportService.java
index 69c48e2..ac4f418 100644
--- a/core/java/com/android/internal/backup/LocalTransportService.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.backup;
+package com.android.localtransport;
import android.app.Service;
import android.content.Intent;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index deb6163..9f38d48 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -137,6 +137,7 @@
UID_UNKNOWN);
params.installerPackageName =
getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ params.installReason = PackageManager.INSTALL_REASON_USER;
File file = new File(mPackageURI.getPath());
try {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 580308a..8c29a25 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -430,9 +430,14 @@
// Check for unknown sources restriction
final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
- if ((unknownSourcesRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+ final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
+ final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
+ & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
+ if (systemRestriction != 0) {
showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
- } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET
+ || unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
} else {
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index c02e3c7..e46ff15 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -103,7 +103,7 @@
<item msgid="3199660090246166812">"Querformat"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"Fehler beim Schreiben in Datei"</string>
- <string name="print_error_default_message" msgid="8602678405502922346">"Fehler. Bitte versuche es erneut."</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"Fehler. Bitte versuche es noch einmal."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Erneut versuchen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Dieser Drucker ist momentan nicht verfügbar."</string>
<string name="print_cannot_load_page" msgid="6179560924492912009">"Vorschau kann nicht angezeigt werden"</string>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 943dd84..89438e5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -14,6 +14,8 @@
static_libs: [
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
+ "SettingsLibAppPreference",
+ "SettingsLibSearchWidget",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp
new file mode 100644
index 0000000..b56181d
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibAppPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ libs: [
+ "androidx.annotation_annotation",
+ "androidx.preference_preference",
+ ],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/AppPreference/AndroidManifest.xml b/packages/SettingsLib/AppPreference/AndroidManifest.xml
new file mode 100644
index 0000000..7e71308
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/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.apppreference">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
new file mode 100644
index 0000000..6d35550
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -0,0 +1,100 @@
+<?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:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="56dp"
+ android:orientation="horizontal"
+ android:paddingEnd="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ 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:style/TextAppearance.Material.Subhead"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:max="100"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:minWidth="64dp"
+ android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/values/dimens.xml b/packages/SettingsLib/AppPreference/res/values/dimens.xml
new file mode 100644
index 0000000..e2a7a19
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/values/dimens.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.
+ -->
+
+<resources>
+ <dimen name="secondary_app_icon_size">32dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java
new file mode 100644
index 0000000..593b6f5
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.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.settingslib.widget.apppreference;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+public class AppPreference extends Preference {
+
+ private int mProgress;
+ private boolean mProgressVisible;
+
+ public AppPreference(Context context) {
+ super(context);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public void setProgress(int amount) {
+ mProgress = amount;
+ mProgressVisible = true;
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+
+ view.findViewById(R.id.summary_container)
+ .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
+ final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
+ if (mProgressVisible) {
+ progress.setProgress(mProgress);
+ progress.setVisibility(View.VISIBLE);
+ } else {
+ progress.setVisibility(View.GONE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml
index d2a7452..731257e 100644
--- a/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="help_feedback_label" msgid="4550436169116444686">"സഹായം & ഫീഡ്ബാക്ക്"</string>
+ <string name="help_feedback_label" msgid="4550436169116444686">"സഹായവും ഫീഡ്ബാക്കും"</string>
</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
index a034d29..2f576e6 100644
--- a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="help_feedback_label" msgid="4550436169116444686">"Help en feedback"</string>
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hulp en feedback"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
new file mode 100644
index 0000000..dced02c
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Geaktiveer deur administrateur"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Gedeaktiveer deur administrateur"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
new file mode 100644
index 0000000..d13be0c
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"በአስተዳዳሪ ነቅቷል"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"በአስተዳዳሪ ተሰናክሏል"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
new file mode 100644
index 0000000..15c0010
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"فعَّل المشرف هذا الإعداد."</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"أوقف المشرف هذا الإعداد."</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
new file mode 100644
index 0000000..7a3b8e4
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"প্ৰশাসকে সক্ষম কৰিছে"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"প্ৰশাসকে অক্ষম কৰিছে"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
new file mode 100644
index 0000000..d1434bd
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Admin tərəfindən aktiv edildi"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Admin tərəfindən deaktiv edildi"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..ee61192
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Administrator je omogućio"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Administrator je onemogućio"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
new file mode 100644
index 0000000..2d13d45
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Уключана адміністратарам"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Адключана адміністратарам"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
new file mode 100644
index 0000000..361910c
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Активирано от администратора"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Деактивирано от администратора"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
new file mode 100644
index 0000000..7609166
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"অ্যাডমিন চালু করেছেন"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"অ্যাডমিন বন্ধ করেছেন"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
new file mode 100644
index 0000000..9704095
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Omogućio administrator"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Onemogućio administrator"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
new file mode 100644
index 0000000..5569664
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Activat per l\'administrador"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Desactivat per l\'administrador"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
new file mode 100644
index 0000000..f8120aa
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Zapnuto administrátorem"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Vypnuto administrátorem"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
new file mode 100644
index 0000000..d75f27b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Aktiveret af administratoren"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Deaktiveret af administratoren"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
new file mode 100644
index 0000000..3ef46d9
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Vom Administrator aktiviert"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Vom Administrator deaktiviert"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
new file mode 100644
index 0000000..643686e
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Ενεργοποιήθηκε από τον διαχειριστή"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Απενεργοποιήθηκε από τον διαχειριστή"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..69c0bc3
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Enabled by admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Disabled by admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..69c0bc3
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Enabled by admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Disabled by admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..69c0bc3
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Enabled by admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Disabled by admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..69c0bc3
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Enabled by admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Disabled by admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rXC/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..f329a76
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rXC/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Enabled by admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Disabled by admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..451ea35
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"El administrador habilitó la opción"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"El administrador inhabilitó la opción"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
new file mode 100644
index 0000000..3407d13
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Habilitada por el administrador"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Inhabilitada por el administrador"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
new file mode 100644
index 0000000..98bad62
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Administraatori lubatud"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Administraatori keelatud"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
new file mode 100644
index 0000000..1641a31
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Administratzaileak gaitu egin du"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Administratzaileak desgaitu egin du"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
new file mode 100644
index 0000000..f4c8334
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"فعالشده توسط سرپرست"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"غیرفعالشده توسط سرپرست"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
new file mode 100644
index 0000000..dd62c1a
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Järjestelmänvalvojan sallima"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Järjestelmänvalvojan estämä"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..85ec907
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Activé par l\'administrateur"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Désactivé par l\'administrateur"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
new file mode 100644
index 0000000..85ec907
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Activé par l\'administrateur"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Désactivé par l\'administrateur"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
new file mode 100644
index 0000000..0d67cc2
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Opción activada polo administrador"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Opción desactivada polo administrador"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
new file mode 100644
index 0000000..076c1be
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"વ્યવસ્થાપકે ચાલુ કરેલ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"વ્યવસ્થાપકે બંધ કરેલ"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
new file mode 100644
index 0000000..75d103f
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"एडमिन की ओर से चालू किया गया"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"एडमिन की ओर से बंद किया गया"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
new file mode 100644
index 0000000..9704095
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Omogućio administrator"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Onemogućio administrator"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
new file mode 100644
index 0000000..be1cb97
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"A rendszergazda bekapcsolta"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"A rendszergazda kikapcsolta"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
new file mode 100644
index 0000000..78dab28
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Միացված է ադմինիստրատորի կողմից"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Անջատվել է ադմինիստրատորի կողմից"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
new file mode 100644
index 0000000..824ddde
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Diaktifkan oleh admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Dinonaktifkan oleh admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
new file mode 100644
index 0000000..0707a67
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Gert virkt af kerfisstjóra"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Gert óvirkt af kerfisstjóra"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
new file mode 100644
index 0000000..084a47b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Attivata dall\'amministratore"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Disattivata dall\'amministratore"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
new file mode 100644
index 0000000..fca5986
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"מופעל על ידי מנהל המכשיר"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"הושבת על ידי מנהל המכשיר"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
new file mode 100644
index 0000000..1c077b6
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"管理者が有効にしました"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"管理者が無効にしました"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
new file mode 100644
index 0000000..9ecf6b3
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"ჩართულია ადმინისტრატორის მიერ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"გათიშულია ადმინისტრატორის მიერ"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
new file mode 100644
index 0000000..552e02e
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Әкімші қосқан"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Әкімші өшірген"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
new file mode 100644
index 0000000..aff5d30
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"បើកដោយអ្នកគ្រប់គ្រង"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"បិទដោយអ្នកគ្រប់គ្រង"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
new file mode 100644
index 0000000..99bd4d5
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"ನಿರ್ವಾಹಕರು ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
new file mode 100644
index 0000000..d7b0b44
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"관리자가 사용 설정함"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"관리자가 사용 중지함"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
new file mode 100644
index 0000000..df3b559
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Администратор иштетип койгон"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Администратор өчүрүп койгон"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
new file mode 100644
index 0000000..fca6d1b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"ເປີດນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"ຖືກຜູ້ເບິ່ງແຍງລະບົບປິດໄວ້"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
new file mode 100644
index 0000000..0bd27bc7
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Įgalino administratorius"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Išjungė administratorius"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
new file mode 100644
index 0000000..9c5f693
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Iespējoja administrators"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Atspējoja administrators"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
new file mode 100644
index 0000000..b0fb0a8
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Овозможено од администраторот"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Оневозможено од администраторот"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
new file mode 100644
index 0000000..dd9c229
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"അഡ്മിൻ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കി"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
new file mode 100644
index 0000000..d32c2ca
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Админ идэвхжүүлсэн"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Админ цуцалсан"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
new file mode 100644
index 0000000..06fdf6f
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"प्रशासकाने सुरू केलेले"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"प्रशासकाने बंद केलेले"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
new file mode 100644
index 0000000..aabfb7c
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Didayakan oleh pentadbir"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Dilumpuhkan oleh pentadbir"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
new file mode 100644
index 0000000..daa4614
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"စီမံခန့်ခွဲသူက ဖွင့်ထားသည်"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"စီမံခန့်ခွဲသူက ပိတ်ထားသည်"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
new file mode 100644
index 0000000..becbb91
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Aktivert av administratoren"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Deaktivert av administratoren"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
new file mode 100644
index 0000000..63ef646
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"प्रशासकद्वारा सक्षम पारिएको"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"प्रशासकद्वारा असक्षम पारिएको"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
new file mode 100644
index 0000000..5d7a85e
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Ingeschakeld door beheerder"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Uitgeschakeld door beheerder"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
new file mode 100644
index 0000000..fee0d39
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"ବ୍ୟବସ୍ଥାପକଙ୍କ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"ବ୍ୟବସ୍ଥାପକଙ୍କ ଦ୍ଵାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
new file mode 100644
index 0000000..2765cabd
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
new file mode 100644
index 0000000..d9e06b6
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Włączone przez administratora"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Wyłączone przez administratora"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..1ad4b6b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Ativado pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Desativado pelo administrador"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..f86a73f
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Ativada pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Desativada pelo administrador"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
new file mode 100644
index 0000000..1ad4b6b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Ativado pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Desativado pelo administrador"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
new file mode 100644
index 0000000..ff8c2aa
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Activat de administrator"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Dezactivat de administrator"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
new file mode 100644
index 0000000..d4dfb1c
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Включено администратором"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Отключено администратором"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
new file mode 100644
index 0000000..7c8520b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"පරිපාලක විසින් සබල කර ඇත"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"ඔබගේ පරිපාලක විසින් අබල කර ඇත"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
new file mode 100644
index 0000000..7a9c1cc
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Povolené správcom"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Zakázané správcom"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
new file mode 100644
index 0000000..4926641
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Omogočil skrbnik"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Onemogočil skrbnik"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
new file mode 100644
index 0000000..5f9dc59
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Aktivizuar nga administratori"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Çaktivizuar nga administratori"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
new file mode 100644
index 0000000..2d1ac54
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Администратор је омогућио"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Администратор је онемогућио"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
new file mode 100644
index 0000000..6af3776
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Aktiverad av administratör"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Inaktiverad av administratören"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
new file mode 100644
index 0000000..08526f8
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Imewashwa na msimamizi"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Imezimwa na msimamizi"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
new file mode 100644
index 0000000..405fb7c
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"நிர்வாகி இயக்கியுள்ளார்"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"நிர்வாகி முடக்கியுள்ளார்"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
new file mode 100644
index 0000000..462f02e
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"నిర్వాహకులు ప్రారంభించారు"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"నిర్వాహకులు నిలిపివేసారు"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
new file mode 100644
index 0000000..e654ba1
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"เปิดใช้โดยผู้ดูแลระบบ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"ปิดใช้โดยผู้ดูแลระบบ"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
new file mode 100644
index 0000000..f80cefa
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Na-enable ng admin"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Na-disable ng admin"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
new file mode 100644
index 0000000..9dd4713
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Yönetici tarafından etkinleştirildi"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Yönetici tarafından devre dışı bırakıldı"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
new file mode 100644
index 0000000..5096921
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Увімкнено адміністратором"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Вимкнено адміністратором"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
new file mode 100644
index 0000000..7e70936
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"منتظم کی طرف سے فعال کردہ"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"منتظم کی طرف سے غیر فعال کردہ"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
new file mode 100644
index 0000000..a402af6
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Administrator tomonidan yoqilgan"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Administrator tomonidan faolsizlantirilgan"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
new file mode 100644
index 0000000..dd993df
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Do quản trị viên bật"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Bị quản trị viên tắt"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..2aab9ac
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"已被管理员启用"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"已被管理员停用"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..cb3163a
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"已由管理員啟用"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"已由管理員停用"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..cb3163a
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"已由管理員啟用"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"已由管理員停用"</string>
+</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
new file mode 100644
index 0000000..313c9eb
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="enabled_by_admin" msgid="7797093432952898000">"Kunikwe amandla umlawuli"</string>
+ <string name="disabled_by_admin" msgid="1910810467107358241">"Kukhutshazwe umlawuli"</string>
+</resources>
diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp
new file mode 100644
index 0000000..7541ca45
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/Android.bp
@@ -0,0 +1,8 @@
+android_library {
+ name: "SettingsLibSearchWidget",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SearchWidget/AndroidManifest.xml b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
new file mode 100644
index 0000000..b86544e
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/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.search">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
new file mode 100644
index 0000000..7e65848
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
+</vector>
diff --git a/packages/SettingsLib/SearchWidget/res/values/strings.xml b/packages/SettingsLib/SearchWidget/res/values/strings.xml
new file mode 100644
index 0000000..0b12810
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR 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">
+ <!-- Text used as a search hint into the search box [CHAR_LIMIT=60]-->
+ <string name="search_menu">Search settings</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index aa96444..b77d4e5 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Ingeprop; kan nie op die oomblik laai nie"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Beheer deur administrateur"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Geaktiveer deur administrateur"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Gedeaktiveer deur administrateur"</string>
<string name="disabled" msgid="9206776641295849915">"Gedeaktiveer"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Toegelaat"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nie toegelaat nie"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 452206c..836ec80 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ተሰክቷል፣ አሁን ኃይል መሙላት አይቻልም"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"በአስተዳዳሪ ቁጥጥር የተደረገበት"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"በአስተዳዳሪ ነቅቷል"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"በአስተዳዳሪ ተሰናክሏል"</string>
<string name="disabled" msgid="9206776641295849915">"ቦዝኗል"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ይፈቀዳል"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"አይፈቀድም"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e67e1ec..5c11db1 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"تم التوصيل، ولكن يتعذّر الشحن الآن"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"إعدادات يتحكم فيها المشرف"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"تم تفعيل الإعداد بواسطة المشرف"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"تم إيقاف الإعداد بواسطة المشرف"</string>
<string name="disabled" msgid="9206776641295849915">"غير مفعّل"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"مسموح به"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"غير مسموح به"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 7311661..ddd17daf 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"প্লাগ কৰি থোৱা হৈছে, এই মুহূৰ্তত চ্চাৰ্জ কৰিব নোৱাৰি"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"পূৰ্ণ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"এডমিনৰ দ্বাৰা নিয়ন্ত্ৰিত"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"প্ৰশাসকে সক্ষম কৰিছে"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"এডমিনে অক্ষম কৰিছে ৰাখিছে"</string>
<string name="disabled" msgid="9206776641295849915">"নিষ্ক্ৰিয়"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"অনুমতি দিয়া হৈছে"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"অনুমতি দিয়া হোৱা নাই"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 750bf5d..a6e484f 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Cihaz hazırda batareya yığa bilmir"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Admin tərəfindən nəzarət olunur"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Admin tərəfindən aktiv edildi"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Admin tərəfindən deaktiv edildi"</string>
<string name="disabled" msgid="9206776641295849915">"Deaktiv"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"İcazə verilib"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"İcazə verilməyib"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index cd8096c..a446845 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Priključeno je, ali punjenje trenutno nije moguće"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontroliše administrator"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Omogućio je administrator"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Administrator je onemogućio"</string>
<string name="disabled" msgid="9206776641295849915">"Onemogućeno"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Dozvoljeno"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nije dozvoljeno"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 072913a..d56db13 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Падключана да сеткі сілкавання, зарадзіць зараз немагчыма"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Поўная"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Кантралюецца адміністратарам"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Уключана адміністратарам"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Адключана адміністратарам"</string>
<string name="disabled" msgid="9206776641295849915">"Адключанае"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Дазволена"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Забаронена"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index a5ce523..c1bc31c 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Включена в захранването, в момента не се зарежда"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролира се от администратор"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Активирано от администратора"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Деактивирано от администратора"</string>
<string name="disabled" msgid="9206776641295849915">"Деактивирано"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Има разрешение"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Няма разрешение"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c053771..1550c00 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"প্লাগ-ইন করা হয়েছে কিন্তু এখনই চার্জ করা যাবে না"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"প্রশাসকের দ্বারা নিয়ন্ত্রিত"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"প্রশাসক দ্বারা সক্ষম করা হয়েছে"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
<string name="disabled" msgid="9206776641295849915">"অক্ষম হয়েছে"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"অনুমোদিত"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"অনুমোদিত নয়"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 764bc19..460e7cf 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Priključen, trenutno se ne može puniti"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Pod kontrolom administratora"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Omogućio administrator"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Onemogućio administrator"</string>
<string name="disabled" msgid="9206776641295849915">"Onemogućeno"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Dozvoljeno"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nije dozvoljeno"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 8309c41a..72dd8cf 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"El dispositiu està endollat però en aquests moments no es pot carregar"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlat per l\'administrador"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Activada per l\'administrador"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Desactivada per l\'administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Desactivat"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Amb permís"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Sense permís"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index bd8bc2b..de02245 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Zapojeno, ale nelze nabíjet"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Spravováno administrátorem"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Zapnuto administrátorem"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Zakázáno administrátorem"</string>
<string name="disabled" msgid="9206776641295849915">"Deaktivováno"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Povoleno"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Není povoleno"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index ab81346..34608d3 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Enheden er tilsluttet en strømkilde. Det er ikke muligt at oplade på nuværende tidspunkt."</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fuldt"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolleret af administratoren"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Aktiveret af administratoren"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Deaktiveret af administratoren"</string>
<string name="disabled" msgid="9206776641295849915">"Deaktiveret"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Tilladt"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ikke tilladt"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 2432a10..79965d2 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -348,7 +348,7 @@
<string name="runningservices_settings_summary" msgid="854608995821032748">"Momentan ausgeführte Dienste anzeigen und steuern"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-Implementierung"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-Implementierung festlegen"</string>
- <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Diese Auswahl ist nicht mehr gültig. Versuche es erneut."</string>
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Diese Auswahl ist nicht mehr gültig. Bitte versuche es noch einmal."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Zu Dateiverschlüsselung wechseln"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Wechseln…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dateiverschlüsselung wird bereits verwendet."</string>
@@ -379,12 +379,12 @@
<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>
<string name="power_remaining_only_more_than_subtext" msgid="8931654680569617380">"Mehr als <xliff:g id="TIME_REMAINING">%1$s</xliff:g> verbleibend"</string>
- <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="1181059207608751924">"Smartphone wird möglicherweise bald ausgeschaltet"</string>
- <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="2606370266981054691">"Tablet wird möglicherweise bald ausgeschaltet"</string>
- <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="2918084807716859985">"Gerät wird möglicherweise bald ausgeschaltet"</string>
- <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="3090926004324573908">"Smartphone wird möglicherweise bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
- <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet wird möglicherweise bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
- <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Gerät wird möglicherweise bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="1181059207608751924">"Smartphone wird eventuell bald ausgeschaltet"</string>
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="2606370266981054691">"Tablet wird eventuell bald ausgeschaltet"</string>
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="2918084807716859985">"Gerät wird eventuell bald ausgeschaltet"</string>
+ <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="3090926004324573908">"Smartphone wird eventuell bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet wird eventuell bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Gerät wird eventuell bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> bis zur vollständigen Aufladung"</string>
<string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis vollständig geladen"</string>
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Angeschlossen, kann derzeit nicht geladen werden"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Durch den Administrator verwaltet"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Vom Administrator aktiviert"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Vom Administrator deaktiviert"</string>
<string name="disabled" msgid="9206776641295849915">"Deaktiviert"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Zulässig"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nicht zulässig"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 6f2889c..b9ea843 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Συνδέθηκε, δεν είναι δυνατή η φόρτιση αυτήν τη στιγμή"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ελέγχονται από το διαχειριστή"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Ενεργοποιήθηκε από τον διαχειριστή"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Απενεργοποιήθηκε από τον διαχειριστή"</string>
<string name="disabled" msgid="9206776641295849915">"Απενεργοποιημένο"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Επιτρέπεται"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Δεν επιτρέπεται"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 154aa7d9..1a7d7c5 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Plugged in, can\'t charge right now"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Enabled by admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Disabled by admin"</string>
<string name="disabled" msgid="9206776641295849915">"Disabled"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Allowed"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Not allowed"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 154aa7d9..1a7d7c5 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Plugged in, can\'t charge right now"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Enabled by admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Disabled by admin"</string>
<string name="disabled" msgid="9206776641295849915">"Disabled"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Allowed"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Not allowed"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 154aa7d9..1a7d7c5 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Plugged in, can\'t charge right now"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Enabled by admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Disabled by admin"</string>
<string name="disabled" msgid="9206776641295849915">"Disabled"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Allowed"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Not allowed"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 154aa7d9..1a7d7c5 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Plugged in, can\'t charge right now"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Enabled by admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Disabled by admin"</string>
<string name="disabled" msgid="9206776641295849915">"Disabled"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Allowed"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Not allowed"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index e9746c0..2bfd5b1 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Plugged in, can\'t charge right now"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Enabled by admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Disabled by admin"</string>
<string name="disabled" msgid="9206776641295849915">"Disabled"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Allowed"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Not allowed"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 706e1d3..790c0d3 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Conectado. No se puede cargar en este momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cargado"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"El administrador habilitó esta opción"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"El administrador inhabilitó esta opción"</string>
<string name="disabled" msgid="9206776641295849915">"Inhabilitada"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Permitida"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"No permitida"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index f9eddd9..1dec85d 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Se ha conectado, pero no se puede cargar en este momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Habilitada por el administrador"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Inhabilitado por el administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Inhabilitada"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Autorizadas"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"No autorizadas"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index ac31880..406e15d 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Vooluvõrgus, praegu ei saa laadida"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Juhib administraator"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Administraatori lubatud"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Administraatori keelatud"</string>
<string name="disabled" msgid="9206776641295849915">"Keelatud"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Lubatud"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Pole lubatud"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2eee948..bd4724e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Konektatuta dago. Ezin da kargatu une honetan."</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Administratzaileak kontrolatzen du"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Administratzaileak gaitu du"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Administratzaileak desgaitu du"</string>
<string name="disabled" msgid="9206776641295849915">"Desgaituta"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Baimena dauka"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ez dauka baimenik"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 6efd4fe..45b8ac7 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"به برق وصل شده است، درحالحاضر شارژ نمیشود"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"توسط سرپرست سیستم کنترل میشود"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"فعالشده توسط سرپرست"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"غیرفعالشده توسط سرپرست"</string>
<string name="disabled" msgid="9206776641295849915">"غیر فعال شد"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"مجاز"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"مجاز نیست"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 4d64e81..23c63b1 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Kytketty virtalähteeseen, lataaminen ei onnistu"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Järjestelmänvalvojan käyttöön ottama"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Järjestelmänvalvojan estämä"</string>
<string name="disabled" msgid="9206776641295849915">"Pois käytöstä"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Sallittu"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ei sallittu"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 8e813ea..ad99000 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"L\'appareil est branché, mais il ne peut pas être chargé pour le moment"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pleine"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Contrôlé par l\'administrateur"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Activé par l\'administrateur"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Désactivé par l\'administrateur"</string>
<string name="disabled" msgid="9206776641295849915">"Désactivée"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Autorisée"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Non autorisée"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index baf8ed0..c23db25 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Appareil branché, mais impossible de le charger pour le moment"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Contrôlé par l\'administrateur"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Activé par l\'administrateur"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Désactivé par l\'administrateur"</string>
<string name="disabled" msgid="9206776641295849915">"Désactivée"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Autorisé"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Non autorisé"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index dc22b25..46532e9 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Conectouse, pero non se pode cargar neste momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Opción controlada polo administrador"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Activado polo administrador"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Desactivado polo administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Desactivada"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Permitida"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Non permitida"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 7943d51..6a74490 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"પ્લગ ઇન કરેલ, હમણાં ચાર્જ કરી શકતા નથી"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"વ્યવસ્થાપકે સક્ષમ કરેલ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"વ્યવસ્થાપકે અક્ષમ કરેલ"</string>
<string name="disabled" msgid="9206776641295849915">"અક્ષમ કર્યો"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"મંજૂરી છે"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"મંજૂરી નથી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 3ec7e58..bd9a6ec 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"प्लग इन है, अभी चार्ज नहीं हो सकती"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"इसका नियंत्रण एडमिन के पास है"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"व्यवस्थापक ने सक्षम किया है"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"व्यवस्थापक ने अक्षम किया है"</string>
<string name="disabled" msgid="9206776641295849915">"बंद किया गया"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"अनुमति है"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"अनुमति नहीं है"</string>
@@ -448,7 +446,6 @@
<string name="alarm_template_far" msgid="3779172822607461675">"अलार्म <xliff:g id="WHEN">%1$s</xliff:g> को बजेगा"</string>
<string name="zen_mode_duration_settings_title" msgid="229547412251222757">"अवधि"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string>
- <!-- no translation found for zen_mode_forever (2704305038191592967) -->
- <skip />
+ <string name="zen_mode_forever" msgid="2704305038191592967">"जब तक आप इसे बंद नहीं करते"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index ce8e6ec..5270531 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Uključen, trenutačno se ne može puniti"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolira administrator"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Omogućio administrator"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Onemogućio administrator"</string>
<string name="disabled" msgid="9206776641295849915">"Onemogućeno"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Dopušteno"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nije dopušteno"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 1c79199..ad897f8 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Csatlakoztatva, jelenleg nem tölt"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Feltöltve"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Rendszergazda által irányítva"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"A rendszergazda bekapcsolta"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"A rendszergazda kikapcsolta"</string>
<string name="disabled" msgid="9206776641295849915">"Letiltva"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Engedélyezett"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nem engedélyezett"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 84ee039..82755f9 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Միացված է հոսանքի աղբյուրին, սակայն այս պահին չի կարող լիցքավորվել"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված է"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Վերահսկվում է ադմինիստրատորի կողմից"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Միացված է ադմինիստրատորի կողմից"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Անջատվել է ադմինիստրատորի կողմից"</string>
<string name="disabled" msgid="9206776641295849915">"Կասեցված է"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Թույլատրված է"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Արգելված է"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 9356c85..961f892 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Tercolok, tidak dapat mengisi baterai sekarang"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Dikontrol oleh admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Diaktifkan oleh admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Dinonaktifkan oleh admin"</string>
<string name="disabled" msgid="9206776641295849915">"Dinonaktifkan"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Diizinkan"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Tidak diizinkan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index ca5e1f5..759b7ba 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Í sambandi, ekki hægt að hlaða eins og er"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Stjórnað af kerfisstjóra"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Gert virkt af kerfisstjóra"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Gert óvirkt af kerfisstjóra"</string>
<string name="disabled" msgid="9206776641295849915">"Óvirkt"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Heimilað"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ekki heimilað"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 88a5bb8..bd41e28 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Collegato alla corrente. Impossibile caricare al momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Gestita dall\'amministratore"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Attivata dall\'amministratore"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Disattivata dall\'amministratore"</string>
<string name="disabled" msgid="9206776641295849915">"Disattivato"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Consentite"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Non consentite"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index cec35cf..5a11e72 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"המכשיר מחובר, אבל לא ניתן לטעון עכשיו"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"נמצא בשליטת מנהל מערכת"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"מופעל על ידי מנהל המכשיר"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"הושבת על ידי מנהל המכשיר"</string>
<string name="disabled" msgid="9206776641295849915">"מושבת"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"מורשה"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"לא מורשה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 9cfdadc..f6b81ed 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"接続されていますが、現在、充電できません"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"フル"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"管理者により管理されています"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"管理者により有効にされています"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"管理者により無効にされています"</string>
<string name="disabled" msgid="9206776641295849915">"無効"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"許可"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"許可しない"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 1083025..d5f5408 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"მიერთებულია, დატენვა ამჟამად ვერ ხერხდება"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"იმართება ადმინისტრატორის მიერ"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"ჩართულია ადმინისტრატორის მიერ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"გათიშულია ადმინისტრატორის მიერ"</string>
<string name="disabled" msgid="9206776641295849915">"გამორთული"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"დაშვებულია"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"დაუშვებელია"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index a122d91..c3fbfcd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Қосылған, зарядталмайды"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Толы"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Әкімші басқарады"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Әкімші қосқан"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Әкімші өшірген"</string>
<string name="disabled" msgid="9206776641295849915">"Өшірілген"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Рұқсат етілген"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Рұқсат етілмеген"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 7ffd766..8e73f81 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ដោតសាកថ្មរួចហើយ ប៉ុន្តែសាកថ្មមិនចូលទេឥឡូវនេះ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"បើកដោយអ្នកគ្រប់គ្រង"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"បិទដោយអ្នកគ្រប់គ្រង"</string>
<string name="disabled" msgid="9206776641295849915">"បិទ"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"បានអនុញ្ញាត"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"មិនអនុញ្ញាតទេ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index e12cbd2..ce225ea 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ಪ್ಲಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆ, ಇದೀಗ ಚಾರ್ಜ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"ನಿರ್ವಾಹಕರು ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="disabled" msgid="9206776641295849915">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ಅನುಮತಿಸಲಾಗಿದೆ"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d29bd65..b47471b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"전원이 연결되었지만 현재 충전할 수 없음"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"관리자가 제어"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"관리자가 사용 설정함"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"관리자가 사용 중지함"</string>
<string name="disabled" msgid="9206776641295849915">"사용 안함"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"허용됨"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"허용되지 않음"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 56b5da1..9853dac4 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Сайылып турат, учурда кубаттоо мүмкүн эмес"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Администратор тарабынан көзөмөлдөнөт"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Администратор иштетип койгон"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Администратор өчүрүп койгон"</string>
<string name="disabled" msgid="9206776641295849915">"Өчүрүлгөн"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Уруксат берилген"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Тыюу салынган"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index eb27f02..1816515 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ສຽບສາຍແລ້ວ, ບໍ່ສາມາດສາກໄດ້ໃນຕອນນີ້"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງ"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"ຜູ້ເບິ່ງແຍງລະບົບເປີດໃຫ້ໃຊ້ແລ້ວ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"ຖືກຜູ້ເບິ່ງແຍງລະບົບປິດໄວ້"</string>
<string name="disabled" msgid="9206776641295849915">"ປິດການນຳໃຊ້"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ອະນຸຍາດແລ້ວ"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ບໍ່ອະນຸຍາດແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 44b532b..e6a8e1c 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Įjungta į maitinimo lizdą, bet šiuo metu įkrauti neįmanoma"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Valdo administratorius"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Įgalino administratorius"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Išjungė administratorius"</string>
<string name="disabled" msgid="9206776641295849915">"Neleidžiama"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Leidžiama"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Neleidžiama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index ba87d07..b55afc0 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Pievienots, taču pašlaik nevar veikt uzlādi"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolē administrators"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Iespējoja administrators"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Atspējoja administrators"</string>
<string name="disabled" msgid="9206776641295849915">"Atspējots"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Atļauts"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nav atļauts"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 0c5c94a..e28c8ee 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Приклучен е, но батеријата не може да се полни во моментов"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Полна"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролирано од администраторот"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Овозможено од администраторот"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Оневозможено од администраторот"</string>
<string name="disabled" msgid="9206776641295849915">"Оневозможено"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Дозволено"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Не е дозволено"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 1a208e62..6860398 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"പ്ലഗ് ഇൻ ചെയ്തു, ഇപ്പോൾ ചാർജ് ചെയ്യാനാവില്ല"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"അഡ്മിൻ നിയന്ത്രിക്കുന്നത്"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"അഡ്മിൻ പ്രവർത്തനക്ഷമമാക്കി"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="disabled" msgid="9206776641295849915">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"അനുവദനീയം"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"അനുവദിച്ചിട്ടില്ല"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 809e82e..ba6d601 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Залгаастай тул одоо цэнэглэх боломжгүй"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Админ удирдсан"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Админ идэвхжүүлсэн"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Админ цуцалсан"</string>
<string name="disabled" msgid="9206776641295849915">"Идэвхгүйжүүлсэн"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Зөвшөөрсөн"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Зөвшөөрөөгүй"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index dfa6d2f..a5d230a 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"प्लग इन केलेले आहे, आता चार्ज करू शकत नाही"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"प्रशासकाने नियंत्रित केलेले"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"प्रशासकाने सक्षम केलेले"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"प्रशासकाने अक्षम केलेले"</string>
<string name="disabled" msgid="9206776641295849915">"अक्षम"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"अनुमती आहे"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"अनुमती नाही"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index b4ed016..a7ac3ef 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Dipalamkan, tidak boleh mengecas sekarang"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Dikawal oleh pentadbir"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Didayakan oleh pentadbir"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Dilumpuhkan oleh pentadbir"</string>
<string name="disabled" msgid="9206776641295849915">"Dilumpuhkan"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Dibenarkan"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Tidak dibenarkan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index c1ffd28..6cdab67 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ပလပ်ထိုးထားသောကြောင့် ယခုအားသွင်း၍ မရသေးပါ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"စီမံခန့်ခွဲသူမှ ထိန်းချုပ်ပါသည်"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"စီမံခန့်ခွဲသူက ဖွင့်ထားသည်"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"စီမံခန့်ခွဲသူက ပိတ်ထားသည်"</string>
<string name="disabled" msgid="9206776641295849915">"ပိတ်ထားပြီး"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ခွင့်ပြုထားသည်"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ခွင့်မပြုပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 5a8e01e..cf18477 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Laderen er koblet til – kan ikke lade akkurat nå"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrollert av administratoren"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Slått på av administratoren"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Avslått av administratoren"</string>
<string name="disabled" msgid="9206776641295849915">"Slått av"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Tillatt"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ikke tillatt"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 72a9abb..8aba0f8 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"प्लगइन गरिएको छ, अहिले नै चार्ज गर्न सकिँदैन"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण चार्ज भएको स्थिति"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"प्रशासकद्वारा नियन्त्रित"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"प्रशासकद्वारा सक्षम पारिएको छ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"प्रशासकद्वारा असक्षम पारिएको छ"</string>
<string name="disabled" msgid="9206776641295849915">"असक्षम पारियो"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"अनुमति छ"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"अनुमति छैन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f341717..e3f1aea 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Aangesloten, kan nu niet opladen"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ingesteld door beheerder"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Ingeschakeld door beheerder"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Uitgeschakeld door beheerder"</string>
<string name="disabled" msgid="9206776641295849915">"Uitgeschakeld"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Toegestaan"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Niet toegestaan"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index a7761e9..8ecac4d 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ପ୍ଲଗ୍ରେ ଲାଗିଛି, ହେଲେ ଏବେ ଚାର୍ଜ କରିପାରିବ ନାହିଁ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ଚାର୍ଜ ଅଛି"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ଆଡ୍ମିନ୍ ଦ୍ୱାରା ନିୟନ୍ତ୍ରିତ"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"ଆଡମିନ୍ଙ୍କ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"ଆଡ୍ମିନ୍ ଦ୍ଵାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="disabled" msgid="9206776641295849915">"ଅକ୍ଷମ ହୋଇଛି"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ଅନୁମୋଦିତ"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ଅନୁମତି ନାହିଁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index c7ab379..6f17e13 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ਪਲੱਗ ਲੱਗਾ ਹੋਇਆ ਹੈ, ਇਸ ਸਮੇਂ ਚਾਰਜ ਨਹੀਂ ਹੋ ਸਕਦੀ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
<string name="disabled" msgid="9206776641295849915">"ਅਯੋਗ ਬਣਾਇਆ"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ਇਜਾਜ਼ਤ ਹੈ"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ਇਜਾਜ਼ਤ ਨਹੀਂ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index effa59a..3c208e3 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Podłączony. Nie można teraz ładować"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolowane przez administratora"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Włączone przez administratora"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Wyłączone przez administratora"</string>
<string name="disabled" msgid="9206776641295849915">"Wyłączone"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Dozwolone"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Niedozwolone"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index afefeda..5c363a4 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Conectado. Não é possível carregar no momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Carregada"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada pelo admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Ativado pelo administrador"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Desativada pelo administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Desativado"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Permitido"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Não permitido"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 4ee4521d..66c59ac 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Ligada à corrente, não é possível carregar neste momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completo"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlado pelo gestor"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Ativada pelo gestor"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Desativada pelo gestor"</string>
<string name="disabled" msgid="9206776641295849915">"Desativada"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Autorizada"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Não autorizada"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index afefeda..5c363a4 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Conectado. Não é possível carregar no momento"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Carregada"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada pelo admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Ativado pelo administrador"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Desativada pelo administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Desativado"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Permitido"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Não permitido"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index f42363e..703add6 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Conectat, nu se poate încărca chiar acum"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlată de administrator"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Activat de administrator"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Dezactivat de administrator"</string>
<string name="disabled" msgid="9206776641295849915">"Dezactivată"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Permise"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nepermise"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 451c710..4eb3ca4 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Подключено, не заряжается"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролируется администратором"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Включено администратором"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Отключено администратором"</string>
<string name="disabled" msgid="9206776641295849915">"Отключено"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Разрешено"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Запрещено"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index d9e69ce..1ab5b79 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"පේනුගත කර ඇත, මේ අවස්ථාවේදී ආරෝපණය කළ නොහැකිය"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"පරිපාලක විසින් පාලනය කරන ලදී"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"පරිපාලක විසින් සබල කර ඇත"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"ඔබගේ පරිපාලක විසින් අබල කර ඇත"</string>
<string name="disabled" msgid="9206776641295849915">"අබල කර ඇත"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ඉඩ දුන්"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ඉඩ නොදෙන"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index a0c1062..5c3920f 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Pripojené, ale nie je možné nabíjať"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ovládané správcom"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Povolené správcom"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Zakázané správcom"</string>
<string name="disabled" msgid="9206776641295849915">"Deaktivované"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Povolené"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nie je povolené"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 3088d0f..f479405 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Priključeno, trenutno ni mogoče polniti"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Nadzira skrbnik"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Omogočil skrbnik"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Onemogočil skrbnik"</string>
<string name="disabled" msgid="9206776641295849915">"Onemogočeno"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Dovoljene"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ni dovoljeno"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 8e31013..2741985 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Në prizë, por nuk mund të ngarkohet për momentin"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolluar nga administratori"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Aktivizuar nga administratori"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Çaktivizuar nga administratori"</string>
<string name="disabled" msgid="9206776641295849915">"Çaktivizuar"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Lejohet"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nuk lejohet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index fe97119..241d9cb 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Прикључено је, али пуњење тренутно није могуће"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Пуна"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролише администратор"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Омогућио је администратор"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Администратор је онемогућио"</string>
<string name="disabled" msgid="9206776641295849915">"Онемогућено"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Дозвољено"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Није дозвољено"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 11f446c..d338e96 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Inkopplad, kan inte laddas just nu"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Strys av administratören"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Aktiverad av administratör"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Inaktiverad av administratören"</string>
<string name="disabled" msgid="9206776641295849915">"Inaktiverad"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Tillåts"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Tillåts inte"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index ba3aaba..df122e2 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Haiwezi kuchaji kwa sasa"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Imedhibitiwa na msimamizi"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Imewashwa na msimamizi"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Imezimwa na msimamizi"</string>
<string name="disabled" msgid="9206776641295849915">"Imezimwa"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Imeruhusiwa"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Hairuhusiwi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 63d5e75..8f5c7d8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"செருகப்பட்டது, ஆனால் இப்போது சார்ஜ் செய்ய முடியவில்லை"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"முழுவதும் சார்ஜ் ஆனது"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"நிர்வாகி இயக்கியுள்ளார்"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"நிர்வாகி முடக்கியுள்ளார்"</string>
<string name="disabled" msgid="9206776641295849915">"முடக்கப்பட்டது"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"அனுமதிக்கப்பட்டது"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"அனுமதிக்கப்படவில்லை"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 4af461d0..5f7ab996 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"ప్లగ్ ఇన్ చేయబడింది, ప్రస్తుతం ఛార్జ్ చేయడం సాధ్యం కాదు"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"నిర్వాహకుని ద్వారా నియంత్రించబడింది"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"నిర్వాహకులు ప్రారంభించారు"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"నిర్వాహకులు నిలిపివేసారు"</string>
<string name="disabled" msgid="9206776641295849915">"నిలిపివేయబడింది"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"అనుమతించినవి"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"అనుమతించబడలేదు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 9cc5c73..020baf0 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"เสียบอยู่ ไม่สามารถชาร์จได้ในขณะนี้"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ผู้ดูแลระบบเป็นผู้ควบคุม"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"เปิดใช้โดยผู้ดูแลระบบ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"ปิดใช้โดยผู้ดูแลระบบ"</string>
<string name="disabled" msgid="9206776641295849915">"ปิดอยู่"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"อนุญาต"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ไม่อนุญาต"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 49abe42..2164ade 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Nakasaksak, hindi makapag-charge sa ngayon"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Pinapamahalaan ng admin"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Na-enable ng admin"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Na-disable ng admin"</string>
<string name="disabled" msgid="9206776641295849915">"Naka-disable"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Pinapayagan"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Hindi pinapayagan"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 23a96d9..532927c 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Prize takıldı, şu anda şarj olamıyor"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Yönetici tarafından denetleniyor"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Yönetici tarafından etkinleştirildi"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Yönetici devre dışı bıraktı"</string>
<string name="disabled" msgid="9206776641295849915">"Devre dışı"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"İzin verildi"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"İzin verilmiyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index cea870a4..cf26d20 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Підключено. Не вдається зарядити"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Керується адміністратором"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Увімкнено адміністратором"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Вимкнено адміністратором"</string>
<string name="disabled" msgid="9206776641295849915">"Вимкнено"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Дозволено"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Заборонено"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 0fb60d9..c158179 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"پلگ ان ہے، ابھی چارج نہیں کر سکتے"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"کنٹرول کردہ بذریعہ منتظم"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"منتظم کی طرف سے فعال کردہ"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"منتظم کی طرف سے غیر فعال کردہ"</string>
<string name="disabled" msgid="9206776641295849915">"غیر فعال"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"اجازت ہے"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"اجازت نہیں ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 4690db3..ac8bffe 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Ulangan, lekin quvvat olmayapti"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Administrator tomonidan boshqariladi"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Administrator tomonidan yoqilgan"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Administrator tomonidan o‘chirilgan"</string>
<string name="disabled" msgid="9206776641295849915">"O‘chiq"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Ruxsat berilgan"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Ruxsat berilmagan"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 937b37d..78306da 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Đã cắm nhưng không thể sạc ngay"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Đầy"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Do quản trị viên kiểm soát"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Kích hoạt bởi quản trị viên"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Bị quản trị viên vô hiệu hóa"</string>
<string name="disabled" msgid="9206776641295849915">"Đã tắt"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Được phép"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Không được phép"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 47753fd..4fb26100 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"已插入电源,但是现在无法充电"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"由管理员控制"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"已被管理员启用"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"已被管理员停用"</string>
<string name="disabled" msgid="9206776641295849915">"已停用"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"允许"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"不允许"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 4cbf5a6..c5a8f920 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"已插入電源插座,但目前無法充電"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"已由管理員停用"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"已由管理員啟用"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"已由管理員停用"</string>
<string name="disabled" msgid="9206776641295849915">"已停用"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"允許"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"不允許"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index cecc6ac..0947ccec 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"已接上電源,但現在無法充電"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"已由管理員停用"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"已由管理員啟用"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"已由管理員停用"</string>
<string name="disabled" msgid="9206776641295849915">"已停用"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"允許"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"不允許"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 94c3abf..cd90d74 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -395,8 +395,6 @@
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Kuxhunyiwe, ayikwazi ukushaja khona manje"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kulawulwa umqondisi"</string>
- <string name="enabled_by_admin" msgid="5302986023578399263">"Kunikwe amandla umlawuli"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Kukhutshazwe umlawuli"</string>
<string name="disabled" msgid="9206776641295849915">"Akusebenzi"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Kuvumelekile"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Akuvumelekile"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 332ced6..508adbd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1116,4 +1116,6 @@
<!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
<string name="time_unit_just_now">Just now</string>
- </resources>
+ <!-- The notice header of Third-party licenses. not translatable -->
+ <string name="notice_header" translatable="false"></string>
+</resources>
diff --git a/packages/SettingsLib/search/Android.mk b/packages/SettingsLib/search/Android.mk
index cb19891..14f9626 100644
--- a/packages/SettingsLib/search/Android.mk
+++ b/packages/SettingsLib/search/Android.mk
@@ -5,6 +5,9 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/main/res
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a710410..58feef5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -102,7 +102,7 @@
BluetoothProfile.A2DP);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index e13e566..988062d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -96,7 +96,7 @@
BluetoothProfile.A2DP_SINK);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index b9f7323..750a843 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -29,16 +29,15 @@
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.VisibleForTesting;
-
import com.android.settingslib.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
+import androidx.annotation.VisibleForTesting;
+
/**
* CachedBluetoothDevice represents a remote Bluetooth device. It contains
* attributes of the device (such as the address, name, RSSI, etc.) and
@@ -48,6 +47,10 @@
public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private static final String TAG = "CachedBluetoothDevice";
+ // See mConnectAttempted
+ private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
+ private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
+
private final Context mContext;
private final BluetoothAdapter mLocalAdapter;
private final LocalBluetoothProfileManager mProfileManager;
@@ -55,7 +58,6 @@
private long mHiSyncId;
// Need this since there is no method for getting RSSI
private short mRssi;
- private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState;
private final List<LocalBluetoothProfile> mProfiles =
new ArrayList<LocalBluetoothProfile>();
@@ -78,17 +80,6 @@
private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
- public long getHiSyncId() {
- return mHiSyncId;
- }
-
- public void setHiSyncId(long id) {
- if (BluetoothUtils.D) {
- Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id);
- }
- mHiSyncId = id;
- }
-
/**
* Last time a bt profile auto-connect was attempted.
* If an ACTION_UUID intent comes in within
@@ -97,14 +88,21 @@
*/
private long mConnectAttempted;
- // See mConnectAttempted
- private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
- private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
-
// Active device state
private boolean mIsActiveDeviceA2dp = false;
private boolean mIsActiveDeviceHeadset = false;
private boolean mIsActiveDeviceHearingAid = false;
+
+ CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager,
+ BluetoothDevice device) {
+ mContext = context;
+ mLocalAdapter = BluetoothAdapter.getDefaultAdapter();
+ mProfileManager = profileManager;
+ mDevice = device;
+ fillData();
+ mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+
/**
* Describes the current device and profile for logging.
*
@@ -133,7 +131,6 @@
}
return;
}
- mProfileConnectionState.put(profile, newProfileState);
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (profile instanceof MapProfile) {
profile.setPreferred(mDevice, true);
@@ -161,18 +158,6 @@
fetchActiveDevices();
}
- CachedBluetoothDevice(Context context,
- LocalBluetoothProfileManager profileManager,
- BluetoothDevice device) {
- mContext = context;
- mLocalAdapter = BluetoothAdapter.getDefaultAdapter();
- mProfileManager = profileManager;
- mDevice = device;
- mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
- fillData();
- mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
- }
-
public void disconnect() {
for (LocalBluetoothProfile profile : mProfiles) {
disconnect(profile);
@@ -204,6 +189,17 @@
connectWithoutResettingTimer(connectAllProfiles);
}
+ public long getHiSyncId() {
+ return mHiSyncId;
+ }
+
+ public void setHiSyncId(long id) {
+ if (BluetoothUtils.D) {
+ Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id);
+ }
+ mHiSyncId = id;
+ }
+
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
@@ -226,7 +222,7 @@
int preferredProfiles = 0;
for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
+ if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
if (profile.isPreferred(mDevice)) {
++preferredProfiles;
connectInt(profile);
@@ -300,14 +296,6 @@
return true;
}
- /**
- * Return true if user initiated pairing on this device. The message text is
- * slightly different for local vs. remote initiated pairing dialogs.
- */
- boolean isUserInitiatedPairing() {
- return mDevice.isBondingInitiatedLocally();
- }
-
public void unpair() {
int state = getBondState();
@@ -332,22 +320,9 @@
}
public int getProfileConnectionState(LocalBluetoothProfile profile) {
- if (mProfileConnectionState.get(profile) == null) {
- // If cache is empty make the binder call to get the state
- int state = profile.getConnectionStatus(mDevice);
- mProfileConnectionState.put(profile, state);
- }
- return mProfileConnectionState.get(profile);
- }
-
- public void clearProfileConnectionState ()
- {
- if (BluetoothUtils.D) {
- Log.d(TAG," Clearing all connection state for dev:" + mDevice.getName());
- }
- for (LocalBluetoothProfile profile :getProfiles()) {
- mProfileConnectionState.put(profile, BluetoothProfile.STATE_DISCONNECTED);
- }
+ return profile != null
+ ? profile.getConnectionStatus(mDevice)
+ : BluetoothProfile.STATE_DISCONNECTED;
}
// TODO: do any of these need to run async on a background thread?
@@ -669,7 +644,7 @@
List<LocalBluetoothProfile> connectableProfiles =
new ArrayList<LocalBluetoothProfile>();
for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.isConnectable()) {
+ if (profile.accessProfileEnabled()) {
connectableProfiles.add(profile);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 5a64e02..21cf0c2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -285,11 +285,6 @@
{
mCachedDevicesMapForHearingAids.remove(cachedDevice.getHiSyncId());
}
- } else {
- // For bonded devices, we need to clear the connection status so that
- // when BT is enabled next time, device connection status shall be retrieved
- // by making a binder call.
- cachedDevice.clearProfileConnectionState();
}
}
for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
@@ -297,11 +292,6 @@
if (notCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
notCachedDevice.setJustDiscovered(false);
mHearingAidDevicesNotAddedInCache.remove(i);
- } else {
- // For bonded devices, we need to clear the connection status so that
- // when BT is enabled next time, device connection status shall be retrieved
- // by making a binder call.
- notCachedDevice.clearProfileConnectionState();
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 2dd8eaf..62507f5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -103,7 +103,7 @@
BluetoothProfile.HEADSET);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 1eeb4f0..8bc0acf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -100,7 +100,7 @@
new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4b6a22c..4879144 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -104,7 +104,7 @@
}
@Override
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index c8d4fc8..61e5b6b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -94,7 +94,7 @@
}
@Override
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index fe6b222..75d16db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -93,7 +93,7 @@
BluetoothProfile.HID_HOST);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
index 0447f37..4b0ca74 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -26,9 +26,9 @@
public interface LocalBluetoothProfile {
/**
- * Returns true if the user can initiate a connection, false otherwise.
+ * Return {@code true} if the user can initiate a connection for this profile in UI.
*/
- boolean isConnectable();
+ boolean accessProfileEnabled();
/**
* Returns true if the user can enable auto connection for this profile.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7000f9d..9653972 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -30,6 +30,7 @@
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPbap;
import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
@@ -98,6 +99,7 @@
private PbapClientProfile mPbapClientProfile;
private PbapServerProfile mPbapProfile;
private HearingAidProfile mHearingAidProfile;
+ private SapProfile mSapProfile;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -210,6 +212,13 @@
addProfile(mPbapClientProfile, PbapClientProfile.NAME,
BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
}
+ if (mSapProfile == null && supportedList.contains(BluetoothProfile.SAP)) {
+ if (DEBUG) {
+ Log.d(TAG, "Adding local SAP profile");
+ }
+ mSapProfile = new SapProfile(mContext, mDeviceManager, this);
+ addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
+ }
mEventManager.registerProfileIntentReceiver();
}
@@ -290,10 +299,11 @@
}
}
- mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
- mProfile.getProfileId());
cachedDevice.onProfileStateChanged(mProfile, newState);
cachedDevice.refresh();
+ // Dispatch profile changed after device update
+ mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
+ mProfile.getProfileId());
}
}
@@ -549,6 +559,11 @@
removedProfiles.remove(mHearingAidProfile);
}
+ if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+ profiles.add(mSapProfile);
+ removedProfiles.remove(mSapProfile);
+ }
+
if (DEBUG) {
Log.d(TAG,"New Profiles" + profiles.toString());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7ad2e28c..1e22f44 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -105,7 +105,7 @@
new MapClientServiceListener(), BluetoothProfile.MAP_CLIENT);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index caea04f..7582024 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -104,7 +104,7 @@
BluetoothProfile.MAP);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index dfd1622..e1e5dbe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -32,7 +32,7 @@
// Order of this profile in device profiles list
private static final int ORDINAL = 2;
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 02afe8d..7b81162 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -78,7 +78,7 @@
BluetoothProfile.PAN);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 8fefb2f..1f15601 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -107,7 +107,7 @@
new PbapClientServiceListener(), BluetoothProfile.PBAP_CLIENT);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index e9d8cb5..adef0841 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -80,7 +80,7 @@
BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 61602c6..b4acc48 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -36,12 +36,10 @@
*/
final class SapProfile implements LocalBluetoothProfile {
private static final String TAG = "SapProfile";
- private static boolean V = true;
private BluetoothSap mService;
private boolean mIsProfileReady;
- private final LocalBluetoothAdapter mLocalAdapter;
private final CachedBluetoothDeviceManager mDeviceManager;
private final LocalBluetoothProfileManager mProfileManager;
@@ -59,7 +57,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) Log.d(TAG,"Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected, profile:" + profile);
mService = (BluetoothSap) proxy;
// We just bound to the service, so refresh the UI for any connected SAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +79,7 @@
}
public void onServiceDisconnected(int profile) {
- if (V) Log.d(TAG,"Bluetooth service disconnected");
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mProfileManager.callServiceDisconnectedListeners();
mIsProfileReady=false;
}
@@ -96,17 +94,15 @@
return BluetoothProfile.SAP;
}
- SapProfile(Context context, LocalBluetoothAdapter adapter,
- CachedBluetoothDeviceManager deviceManager,
+ SapProfile(Context context, CachedBluetoothDeviceManager deviceManager,
LocalBluetoothProfileManager profileManager) {
- mLocalAdapter = adapter;
mDeviceManager = deviceManager;
mProfileManager = profileManager;
- mLocalAdapter.getProfileProxy(context, new SapServiceListener(),
+ BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new SapServiceListener(),
BluetoothProfile.SAP);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
@@ -115,50 +111,47 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> sinks = mService.getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }
+ if (mService == null) {
+ return false;
}
return mService.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
- if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
- }
- return mService.disconnect(device);
- } else {
+ if (mService == null) {
return false;
}
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
}
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
- return !deviceList.isEmpty() && deviceList.get(0).equals(device)
- ? mService.getConnectionState(device)
- : BluetoothProfile.STATE_DISCONNECTED;
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
return mService.getPriority(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -169,7 +162,9 @@
}
public List<BluetoothDevice> getConnectedDevices() {
- if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
return mService.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
@@ -207,11 +202,11 @@
}
protected void finalize() {
- if (V) Log.d(TAG, "finalize()");
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP,
- mService);
+ mService);
mService = null;
}catch (Throwable t) {
Log.w(TAG, "Error cleaning up SAP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 42306f6..9db4a35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -46,7 +46,7 @@
* TODO: Remove duplicate codes once backward support ends.
*/
class LicenseHtmlGeneratorFromXml {
- private static final String TAG = "LicenseHtmlGeneratorFromXml";
+ private static final String TAG = "LicenseGeneratorFromXml";
private static final String TAG_ROOT = "licenses";
private static final String TAG_FILE_NAME = "file-name";
@@ -107,12 +107,13 @@
mXmlFiles = xmlFiles;
}
- public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+ public static boolean generateHtml(List<File> xmlFiles, File outputFile,
+ String noticeHeader) {
LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
- return genertor.generateHtml(outputFile);
+ return genertor.generateHtml(outputFile, noticeHeader);
}
- private boolean generateHtml(File outputFile) {
+ private boolean generateHtml(File outputFile, String noticeHeader) {
for (File xmlFile : mXmlFiles) {
parse(xmlFile);
}
@@ -125,7 +126,8 @@
try {
writer = new PrintWriter(outputFile);
- generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+ generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer,
+ noticeHeader);
writer.flush();
writer.close();
@@ -239,13 +241,18 @@
@VisibleForTesting
static void generateHtml(Map<String, String> fileNameToContentIdMap,
- Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+ Map<String, String> contentIdToFileContentMap, PrintWriter writer,
+ String noticeHeader) {
List<String> fileNameList = new ArrayList();
fileNameList.addAll(fileNameToContentIdMap.keySet());
Collections.sort(fileNameList);
writer.println(HTML_HEAD_STRING);
+ if (!TextUtils.isEmpty(noticeHeader)) {
+ writer.println(noticeHeader);
+ }
+
int count = 0;
Map<String, Integer> contentIdToOrderMap = new HashMap();
List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 3930069..78e807c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -60,7 +60,7 @@
File cachedHtmlFile = getCachedHtmlFile(mContext);
if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
- || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
return cachedHtmlFile;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 360c19c..ca62485 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.util.Log;
+import com.android.settingslib.R;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.io.File;
@@ -65,7 +66,7 @@
File cachedHtmlFile = getCachedHtmlFile(mContext);
if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
- || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
return cachedHtmlFile;
}
@@ -101,7 +102,8 @@
return outdated;
}
- static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
- return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+ static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
+ return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile,
+ context.getString(R.string.notice_header));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
index c3241bb..e9c5238 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
@@ -36,7 +36,13 @@
/**
* 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";
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java
new file mode 100644
index 0000000..9b3ff8b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Usage data in a billing cycle with bucketized data for plotting the usage chart.
+ */
+public class NetworkCycleChartData extends NetworkCycleData {
+ public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1);
+
+ private List<NetworkCycleData> mUsageBuckets;
+
+ private NetworkCycleChartData() {
+ }
+
+ public List<NetworkCycleData> getUsageBuckets() {
+ return mUsageBuckets;
+ }
+
+ public static class Builder extends NetworkCycleData.Builder {
+ private NetworkCycleChartData mObject = new NetworkCycleChartData();
+
+ public Builder setUsageBuckets(List<NetworkCycleData> buckets) {
+ getObject().mUsageBuckets = buckets;
+ return this;
+ }
+
+ @Override
+ protected NetworkCycleChartData getObject() {
+ return mObject;
+ }
+
+ @Override
+ public NetworkCycleChartData build() {
+ return getObject();
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
new file mode 100644
index 0000000..7ae3398
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.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.settingslib.net;
+
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle with
+ * bucketized usages.
+ */
+public class NetworkCycleChartDataLoader
+ extends NetworkCycleDataLoader<List<NetworkCycleChartData>> {
+
+ private static final String TAG = "NetworkCycleChartLoader";
+
+ private final List<NetworkCycleChartData> mData;
+
+ private NetworkCycleChartDataLoader(Builder builder) {
+ super(builder);
+ mData = new ArrayList<NetworkCycleChartData>();
+ }
+
+ @Override
+ void recordUsage(long start, long end) {
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(
+ mNetworkType, mSubId, start, end);
+ final long total = getTotalUsage(stats);
+ if (total > 0L) {
+ final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
+ builder.setUsageBuckets(getUsageBuckets(start, end))
+ .setStartTime(start)
+ .setEndTime(end)
+ .setTotalUsage(total);
+ mData.add(builder.build());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ }
+ }
+
+ @Override
+ List<NetworkCycleChartData> getCycleUsage() {
+ return mData;
+ }
+
+ public static Builder<?> builder(Context context) {
+ return new Builder<NetworkCycleChartDataLoader>(context) {
+ @Override
+ public NetworkCycleChartDataLoader build() {
+ return new NetworkCycleChartDataLoader(this);
+ }
+ };
+ }
+
+ private List<NetworkCycleData> getUsageBuckets(long start, long end) {
+ final List<NetworkCycleData> data = new ArrayList<>();
+ long bucketStart = start;
+ long bucketEnd = start + NetworkCycleChartData.BUCKET_DURATION_MS;
+ while (bucketEnd <= end) {
+ long usage = 0L;
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(
+ mNetworkType, mSubId, bucketStart, bucketEnd);
+ usage = getTotalUsage(stats);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ }
+ data.add(new NetworkCycleData.Builder()
+ .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build());
+ bucketStart = bucketEnd;
+ bucketEnd += NetworkCycleChartData.BUCKET_DURATION_MS;
+ }
+ return data;
+ }
+
+ public static abstract class Builder<T extends NetworkCycleChartDataLoader>
+ extends NetworkCycleDataLoader.Builder<T> {
+
+ public Builder(Context context) {
+ super(context);
+ }
+
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
new file mode 100644
index 0000000..26c65a2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+/**
+ * Base data structure representing usage data in a billing cycle.
+ */
+public class NetworkCycleData {
+
+ private long mStartTime;
+ private long mEndTime;
+ private long mTotalUsage;
+
+ protected NetworkCycleData() {
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getEndTime() {
+ return mEndTime;
+ }
+
+ public long getTotalUsage() {
+ return mTotalUsage;
+ }
+
+ public static class Builder {
+
+ private NetworkCycleData mObject = new NetworkCycleData();
+
+ public Builder setStartTime(long start) {
+ getObject().mStartTime = start;
+ return this;
+ }
+
+ public Builder setEndTime(long end) {
+ getObject().mEndTime = end;
+ return this;
+ }
+
+ public Builder setTotalUsage(long total) {
+ getObject().mTotalUsage = total;
+ return this;
+ }
+
+ protected NetworkCycleData getObject() {
+ return mObject;
+ }
+
+ public NetworkCycleData build() {
+ return getObject();
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java
new file mode 100644
index 0000000..9d13717
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.util.concurrent.TimeUnit;
+
+/**
+ * Usage data in a billing cycle for a specific Uid.
+ */
+public class NetworkCycleDataForUid extends NetworkCycleData {
+
+ private long mBackgroudUsage;
+ private long mForegroudUsage;
+
+ private NetworkCycleDataForUid() {
+ }
+
+ public long getBackgroudUsage() {
+ return mBackgroudUsage;
+ }
+
+ public long getForegroudUsage() {
+ return mForegroudUsage;
+ }
+
+ public static class Builder extends NetworkCycleData.Builder {
+
+ private NetworkCycleDataForUid mObject = new NetworkCycleDataForUid();
+
+ public Builder setBackgroundUsage(long backgroundUsage) {
+ getObject().mBackgroudUsage = backgroundUsage;
+ return this;
+ }
+
+ public Builder setForegroundUsage(long foregroundUsage) {
+ getObject().mForegroudUsage = foregroundUsage;
+ return this;
+ }
+
+ @Override
+ public NetworkCycleDataForUid getObject() {
+ return mObject;
+ }
+
+ @Override
+ public NetworkCycleDataForUid build() {
+ return getObject();
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
new file mode 100644
index 0000000..95efb4c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle for a
+ * specific Uid.
+ */
+public class NetworkCycleDataForUidLoader extends
+ NetworkCycleDataLoader<List<NetworkCycleDataForUid>> {
+ private static final String TAG = "NetworkDataForUidLoader";
+
+ private final List<NetworkCycleDataForUid> mData;
+ private final int mUid;
+
+ private NetworkCycleDataForUidLoader(Builder builder) {
+ super(builder);
+ mUid = builder.mUid;
+ mData = new ArrayList<NetworkCycleDataForUid>();
+ }
+
+ @Override
+ void recordUsage(long start, long end) {
+ try {
+ final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid(
+ mNetworkType, mSubId, start, end, mUid);
+ final long total = getTotalUsage(stats);
+ if (total > 0L) {
+ final long foreground = getForegroundUsage(start, end);
+ final NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder();
+ builder.setBackgroundUsage(total - foreground)
+ .setForegroundUsage(foreground)
+ .setStartTime(start)
+ .setEndTime(end)
+ .setTotalUsage(total);
+ mData.add(builder.build());
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ }
+ }
+
+ @Override
+ List<NetworkCycleDataForUid> getCycleUsage() {
+ return mData;
+ }
+
+ public static Builder<?> builder(Context context) {
+ return new Builder<NetworkCycleDataForUidLoader>(context) {
+ @Override
+ public NetworkCycleDataForUidLoader build() {
+ return new NetworkCycleDataForUidLoader(this);
+ }
+ };
+ }
+
+ private long getForegroundUsage(long start, long end) {
+ final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
+ mNetworkType, mSubId, start, end, mUid, TAG_NONE, STATE_FOREGROUND);
+ return getTotalUsage(stats);
+ }
+
+ public static abstract class Builder<T extends NetworkCycleDataForUidLoader>
+ extends NetworkCycleDataLoader.Builder<T> {
+
+ private int mUid;
+
+ public Builder(Context context) {
+ super(context);
+ }
+
+ public Builder<T> setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
new file mode 100644
index 0000000..cc936d2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.DateUtils;
+import android.util.Pair;
+
+import java.time.ZonedDateTime;
+import java.util.Iterator;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle.
+ */
+public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
+ private static final String TAG = "NetworkCycleDataLoader";
+ protected final NetworkStatsManager mNetworkStatsManager;
+ protected final String mSubId;
+ protected final int mNetworkType;
+ private final NetworkPolicy mPolicy;
+ private final NetworkTemplate mNetworkTemplate;
+ @VisibleForTesting
+ final INetworkStatsService mNetworkStatsService;
+
+ protected NetworkCycleDataLoader(Builder<?> builder) {
+ super(builder.mContext);
+ mPolicy = builder.mPolicy;
+ mSubId = builder.mSubId;
+ mNetworkType = builder.mNetworkType;
+ mNetworkTemplate = builder.mNetworkTemplate;
+ mNetworkStatsManager = (NetworkStatsManager)
+ builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
+ mNetworkStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ public D loadInBackground() {
+ if (mPolicy == null) {
+ loadFourWeeksData();
+ } else {
+ loadPolicyData();
+ }
+ return getCycleUsage();
+ }
+
+ @VisibleForTesting
+ void loadPolicyData() {
+ final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator =
+ NetworkPolicyManager.cycleIterator(mPolicy);
+ while (iterator.hasNext()) {
+ final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next();
+ final long cycleStart = cycle.first.toInstant().toEpochMilli();
+ final long cycleEnd = cycle.second.toInstant().toEpochMilli();
+ recordUsage(cycleStart, cycleEnd);
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+
+ @VisibleForTesting
+ void loadFourWeeksData() {
+ try {
+ final INetworkStatsSession networkSession = mNetworkStatsService.openSession();
+ final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork(
+ mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES);
+ final long historyStart = networkHistory.getStart();
+ final long historyEnd = networkHistory.getEnd();
+
+ long cycleEnd = historyEnd;
+ while (cycleEnd > historyStart) {
+ final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
+ recordUsage(cycleStart, cycleEnd);
+ cycleEnd = cycleStart;
+ }
+
+ TrafficStats.closeQuietly(networkSession);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @VisibleForTesting
+ abstract void recordUsage(long start, long end);
+
+ abstract D getCycleUsage();
+
+ public static Builder<?> builder(Context context) {
+ return new Builder<NetworkCycleDataLoader>(context) {
+ @Override
+ public NetworkCycleDataLoader build() {
+ return null;
+ }
+ };
+ }
+
+ protected long getTotalUsage(NetworkStats stats) {
+ long bytes = 0L;
+ if (stats != null) {
+ final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ while (stats.hasNextBucket() && stats.getNextBucket(bucket)) {
+ bytes += bucket.getRxBytes() + bucket.getTxBytes();
+ }
+ stats.close();
+ }
+ return bytes;
+ }
+
+ public static abstract class Builder<T extends NetworkCycleDataLoader> {
+ private final Context mContext;
+ private NetworkPolicy mPolicy;
+ private String mSubId;
+ private int mNetworkType;
+ private NetworkTemplate mNetworkTemplate;
+
+ public Builder (Context context) {
+ mContext = context;
+ }
+
+ public Builder<T> setNetworkPolicy(NetworkPolicy policy) {
+ mPolicy = policy;
+ return this;
+ }
+
+ public Builder<T> setSubscriberId(String subId) {
+ mSubId = subId;
+ return this;
+ }
+
+ public Builder<T> setNetworkType(int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ public Builder<T> setNetworkTemplate(NetworkTemplate template) {
+ mNetworkTemplate = template;
+ return this;
+ }
+
+ public abstract T build();
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
similarity index 77%
rename from packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
rename to packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
index a070b2a..34e6097 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
@@ -20,25 +20,23 @@
import android.app.usage.NetworkStats;
import android.content.Context;
import android.os.RemoteException;
-import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.loader.content.AsyncTaskLoader;
/**
- * Loader for retrieving the network stats details for all UIDs.
+ * Loader for retrieving the network stats summary for all UIDs.
*/
-public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> {
+public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
private static final String TAG = "NetworkDetailLoader";
private final NetworkStatsManager mNetworkStatsManager;
- private final TelephonyManager mTelephonyManager;
private final long mStart;
private final long mEnd;
- private final int mSubId;
+ private final String mSubId;
private final int mNetworkType;
- private NetworkStatsDetailLoader(Builder builder) {
+ private NetworkStatsSummaryLoader(Builder builder) {
super(builder.mContext);
mStart = builder.mStart;
mEnd = builder.mEnd;
@@ -46,8 +44,6 @@
mNetworkType = builder.mNetworkType;
mNetworkStatsManager = (NetworkStatsManager)
builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
- mTelephonyManager =
- (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
@Override
@@ -59,8 +55,7 @@
@Override
public NetworkStats loadInBackground() {
try {
- return mNetworkStatsManager.queryDetails(
- mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd);
+ return mNetworkStatsManager.querySummary(mNetworkType, mSubId, mStart, mEnd);
} catch (RemoteException e) {
Log.e(TAG, "Exception querying network detail.", e);
return null;
@@ -83,7 +78,7 @@
private final Context mContext;
private long mStart;
private long mEnd;
- private int mSubId;
+ private String mSubId;
private int mNetworkType;
public Builder(Context context) {
@@ -100,7 +95,7 @@
return this;
}
- public Builder setSubscriptionId(int subId) {
+ public Builder setSubscriberId(String subId) {
mSubId = subId;
return this;
}
@@ -110,8 +105,8 @@
return this;
}
- public NetworkStatsDetailLoader build() {
- return new NetworkStatsDetailLoader(this);
+ public NetworkStatsSummaryLoader build() {
+ return new NetworkStatsSummaryLoader(this);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index fe0b35b..089f773 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -163,6 +163,12 @@
? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi);
}
+ /** Refresh the status label on Locale changed. */
+ public void refreshLocale() {
+ updateStatusLabel();
+ mCallback.run();
+ }
+
private String getValidSsid(WifiInfo info) {
String ssid = info.getSSID();
if (ssid != null && !WifiSsid.NONE.equals(ssid)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 9050b4b..0dbc037 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -81,7 +81,7 @@
private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS;
/** Maximum age of scan results to hold onto while actively scanning. **/
- private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000;
+ private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
private static final String TAG = "WifiTracker";
private static final boolean DBG() {
@@ -134,8 +134,8 @@
/**
* Tracks whether fresh scan results have been received since scanning start.
*
- * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks
- * so that we do not update the UI with stale data / clear out existing UI elements prematurely.
+ * <p>If this variable is false, we will not invoke callbacks so that we do not
+ * update the UI with stale data / clear out existing UI elements prematurely.
*/
private boolean mStaleScanResults = true;
@@ -327,7 +327,8 @@
* <p>Intended to only be invoked within {@link #onStart()}.
*/
@MainThread
- private void forceUpdate() {
+ @VisibleForTesting
+ void forceUpdate() {
mLastInfo = mWifiManager.getConnectionInfo();
mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
@@ -443,10 +444,8 @@
mScanResultCache.put(newResult.BSSID, newResult);
}
- // Don't evict old results if no new scan results
- if (!mStaleScanResults) {
- evictOldScans();
- }
+ // Evict old results in all conditions
+ evictOldScans();
ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>();
for (ScanResult result : mScanResultCache.values()) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 517db78..1860b31 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
@@ -104,6 +103,10 @@
private static final byte SCORE_2 = 15;
private static final int BADGE_2 = AccessPoint.Speed.FAST;
+ private static final String SSID_3 = "ssid3";
+ private static final String BSSID_3 = "CC:00:00:00:00:00";
+ private static final int RSSI_3 = -40;
+
// TODO(b/65594609): Convert mutable Data objects to instance variables / builder pattern
private static final int NETWORK_ID_1 = 123;
private static final int CONNECTED_RSSI = -50;
@@ -255,6 +258,19 @@
SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
}
+ private static ScanResult buildStaleScanResult() {
+ return new ScanResult(
+ WifiSsid.createFromAsciiEncoded(SSID_3),
+ BSSID_3,
+ 0, // hessid
+ 0, //anqpDomainId
+ null, // osuProviders
+ "", // capabilities
+ RSSI_3,
+ 0, // frequency
+ 0 /* microsecond timestamp */);
+ }
+
private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(
Intent ... intents)
throws InterruptedException {
@@ -896,4 +912,18 @@
assertThat(aps.get(0).isReachable()).isTrue();
assertThat(aps.get(1).isReachable()).isTrue();
}
+
+ @Test
+ public void onStart_updateScanResults_evictOldScanResult() {
+ when(mockWifiManager.getScanResults()).thenReturn(
+ Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult()));
+ WifiTracker tracker = createMockedWifiTracker();
+
+ tracker.forceUpdate();
+
+ // Only has scanResult1 and scanResult2
+ assertThat(tracker.getAccessPoints()).hasSize(2);
+ assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+ assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index 0ca2e87..ede248b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -52,6 +52,7 @@
@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"));
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 7baded8..62b5688 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
@@ -118,7 +118,7 @@
* Test to verify addDevice().
*/
@Test
- public void testAddDevice_validCachedDevices_devicesAdded() {
+ public void addDevice_validCachedDevices_devicesAdded() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -136,7 +136,7 @@
* Test to verify getName().
*/
@Test
- public void testGetName_validCachedDevice_nameFound() {
+ public void getName_validCachedDevice_nameFound() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
@@ -146,7 +146,7 @@
* Test to verify onDeviceNameUpdated().
*/
@Test
- public void testOnDeviceNameUpdated_validName_nameUpdated() {
+ public void onDeviceNameUpdated_validName_nameUpdated() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
@@ -161,7 +161,7 @@
* Test to verify clearNonBondedDevices().
*/
@Test
- public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
+ public void clearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -193,7 +193,7 @@
* Test to verify clearNonBondedDevices() for hearing aids.
*/
@Test
- public void testClearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() {
+ public void clearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
@@ -214,7 +214,7 @@
* Test to verify onHiSyncIdChanged() for hearing aid devices with same HiSyncId.
*/
@Test
- public void testOnHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
+ public void onHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -247,7 +247,7 @@
* device is connected and other is disconnected. The connected device should be chosen.
*/
@Test
- public void testOnHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
+ public void onHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -282,7 +282,7 @@
* Test to verify onHiSyncIdChanged() for hearing aid devices with different HiSyncId.
*/
@Test
- public void testOnHiSyncIdChanged_differentHiSyncId_populateInSameList() {
+ public void onHiSyncIdChanged_differentHiSyncId_populateInSameList() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -316,7 +316,7 @@
* Test to verify onProfileConnectionStateChanged() for single hearing aid device connection.
*/
@Test
- public void testOnProfileConnectionStateChanged_singleDeviceConnected_visible() {
+ public void onProfileConnectionStateChanged_singleDeviceConnected_visible() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -353,7 +353,7 @@
* devices are disconnected and they get connected.
*/
@Test
- public void testOnProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
+ public void onProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -405,7 +405,7 @@
* devices are connected and they get disconnected.
*/
@Test
- public void testOnProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
+ public void onProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -458,7 +458,7 @@
* Test to verify OnDeviceUnpaired() for a paired hearing Aid device pair.
*/
@Test
- public void testOnDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
+ public void onDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -488,7 +488,7 @@
* Test to verify OnDeviceUnpaired() for paired hearing Aid devices which are not a pair.
*/
@Test
- public void testOnDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
+ public void onDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -532,7 +532,7 @@
* Test to verify addDevice() for hearing aid devices with same HiSyncId.
*/
@Test
- public void testAddDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() {
+ public void addDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() {
doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
.getHearingAidProfile();
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
@@ -560,7 +560,7 @@
* Test to verify addDevice() for hearing aid devices with different HiSyncId.
*/
@Test
- public void testAddDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() {
+ public void addDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() {
doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
.getHearingAidProfile();
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
@@ -592,7 +592,7 @@
* Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with same HiSyncId.
*/
@Test
- public void testGetHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() {
+ public void getHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() {
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
@@ -609,7 +609,7 @@
* HiSyncId.
*/
@Test
- public void testGetHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() {
+ public void getHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() {
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID2);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
@@ -625,7 +625,7 @@
* Test to verify updateHearingAidsDevices().
*/
@Test
- public void testUpdateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() {
+ public void updateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() {
doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
.getHearingAidProfile();
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
@@ -643,7 +643,7 @@
* Test to verify onBtClassChanged().
*/
@Test
- public void testOnBtClassChanged_validBtClass_classChanged() {
+ public void onBtClassChanged_validBtClass_classChanged() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);
@@ -658,7 +658,7 @@
* Test to verify onDeviceDisappeared().
*/
@Test
- public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
+ public void onDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
@@ -673,7 +673,7 @@
* Test to verify onActiveDeviceChanged().
*/
@Test
- public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
+ public void onActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -736,7 +736,7 @@
* Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
*/
@Test
- public void testOnActiveDeviceChanged_withA2dpAndHearingAid() {
+ public void onActiveDeviceChanged_withA2dpAndHearingAid() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
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 c18db11..f6201dd 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
@@ -20,6 +20,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -79,74 +80,74 @@
}
@Test
- public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
+ public void getConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set PAN profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with battery level
mBatteryLevel = 10;
// Set PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
// Set PAN profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set PAN profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@Test
- public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() {
+ public void getConnectionSummary_testMultipleProfileConnectDisconnect() {
mBatteryLevel = 10;
// Set HFP, A2DP and PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
// Disconnect HFP only and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"10% battery");
// Disconnect A2DP only and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"10% battery");
// Disconnect both HFP and A2DP and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"10% battery");
// Disconnect all profiles and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@Test
- public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+ public void getConnectionSummary_testSingleProfileActiveDeviceA2dp() {
// Test without battery level
// Set A2DP profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for A2DP and test connection state summary
@@ -159,26 +160,26 @@
"Active, 10% battery");
// Set A2DP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set A2DP profile to be connected, Active and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set A2DP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@Test
- public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() {
+ public void getConnectionSummary_testSingleProfileActiveDeviceHfp() {
// Test without battery level
// Set HFP profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for HFP and test connection state summary
@@ -193,26 +194,26 @@
"Active, 10% battery");
// Set HFP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set HFP profile to be connected, Active and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set HFP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@Test
- public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+ public void getConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
// Test without battery level
// Set Hearing Aid profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for Hearing Aid and test connection state summary
@@ -227,11 +228,11 @@
}
@Test
- public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
+ public void getConnectionSummary_testMultipleProfilesActiveDevice() {
// Test without battery level
// Set A2DP and HFP profiles to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for A2DP and HFP and test connection state summary
@@ -246,14 +247,14 @@
// Disconnect A2DP only and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"10% battery");
// Disconnect HFP only and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"Active, 10% battery");
@@ -261,15 +262,15 @@
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set A2DP and HFP profiles to be connected, Active and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Set A2DP and HFP profiles to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@@ -277,32 +278,32 @@
public void getCarConnectionSummary_singleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
// Test with battery level
mBatteryLevel = 10;
// Set PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%");
// Set PAN profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
}
@@ -311,29 +312,29 @@
mBatteryLevel = 10;
// Set HFP, A2DP and PAN profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%");
// Disconnect HFP only and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
"Connected (no phone), battery 10%");
// Disconnect A2DP only and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
"Connected (no media), battery 10%");
// Disconnect both HFP and A2DP and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
"Connected (no phone or media), battery 10%");
// Disconnect all profiles and test connection state summary
- mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
}
@@ -341,7 +342,7 @@
public void getCarConnectionSummary_singleProfileActiveDeviceA2dp() {
// Test without battery level
// Set A2DP profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
// Set device as Active for A2DP and test connection state summary
@@ -354,18 +355,18 @@
"Connected, battery 10%, active (media)");
// Set A2DP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set A2DP profile to be connected, Active and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (media)");
// Set A2DP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
}
@@ -373,7 +374,7 @@
public void getCarConnectionSummary_singleProfileActiveDeviceHfp() {
// Test without battery level
// Set HFP profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
// Set device as Active for HFP and test connection state summary
@@ -386,18 +387,18 @@
"Connected, battery 10%, active (phone)");
// Set HFP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set HFP profile to be connected, Active and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (phone)");
// Set HFP profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
}
@@ -405,7 +406,7 @@
public void getCarConnectionSummary_singleProfileActiveDeviceHearingAid() {
// Test without battery level
// Set Hearing Aid profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
// Set device as Active for Hearing Aid and test connection state summary
@@ -414,8 +415,7 @@
// Set Hearing Aid profile to be disconnected and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
- mCachedDevice.onProfileStateChanged(mHearingAidProfile,
- BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
}
@@ -423,8 +423,8 @@
public void getCarConnectionSummary_multipleProfilesActiveDevice() {
// Test without battery level
// Set A2DP and HFP profiles to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
// Set device as Active for A2DP and HFP and test connection state summary
@@ -439,14 +439,14 @@
// Disconnect A2DP only and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
"Connected (no media), battery 10%, active (phone)");
// Disconnect HFP only and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
"Connected (no phone), battery 10%, active (media)");
@@ -454,21 +454,21 @@
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
// Set A2DP and HFP profiles to be connected, Active and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active");
// Set A2DP and HFP profiles to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
}
@Test
- public void testDeviceName_testAliasNameAvailable() {
+ public void deviceName_testAliasNameAvailable() {
when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
when(mDevice.getName()).thenReturn(DEVICE_NAME);
CachedBluetoothDevice cachedBluetoothDevice =
@@ -480,7 +480,7 @@
}
@Test
- public void testDeviceName_testNameNotAvailable() {
+ public void deviceName_testNameNotAvailable() {
CachedBluetoothDevice cachedBluetoothDevice =
new CachedBluetoothDevice(mContext, mProfileManager, mDevice);
// Verify device address is returned on getName
@@ -490,7 +490,7 @@
}
@Test
- public void testDeviceName_testRenameDevice() {
+ public void deviceName_testRenameDevice() {
final String[] alias = {DEVICE_ALIAS};
doAnswer(invocation -> alias[0]).when(mDevice).getAliasName();
doAnswer(invocation -> {
@@ -513,7 +513,7 @@
}
@Test
- public void testSetActive() {
+ public void setActive() {
when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
@@ -521,19 +521,19 @@
assertThat(mCachedDevice.setActive()).isFalse();
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.setActive()).isTrue();
- mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.setActive()).isTrue();
- mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.setActive()).isFalse();
}
@Test
- public void testIsA2dpDevice_isA2dpDevice() {
+ public void isA2dpDevice_isA2dpDevice() {
when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mA2dpProfile.getConnectionStatus(mDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
@@ -542,7 +542,7 @@
}
@Test
- public void testIsA2dpDevice_isNotA2dpDevice() {
+ public void isA2dpDevice_isNotA2dpDevice() {
when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mA2dpProfile.getConnectionStatus(mDevice)).
thenReturn(BluetoothProfile.STATE_DISCONNECTING);
@@ -551,7 +551,7 @@
}
@Test
- public void testIsHfpDevice_isHfpDevice() {
+ public void isHfpDevice_isHfpDevice() {
when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
when(mHfpProfile.getConnectionStatus(mDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
@@ -637,4 +637,24 @@
verify(mDevice, never()).setAlias(any());
}
+
+ @Test
+ public void getProfileConnectionState_nullProfile_returnDisconnected() {
+ assertThat(mCachedDevice.getProfileConnectionState(null)).isEqualTo(
+ BluetoothProfile.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void getProfileConnectionState_profileConnected_returnConnected() {
+ doReturn(BluetoothProfile.STATE_CONNECTED).when(mA2dpProfile).getConnectionStatus(
+ any(BluetoothDevice.class));
+
+ assertThat(mCachedDevice.getProfileConnectionState(mA2dpProfile)).isEqualTo(
+ BluetoothProfile.STATE_CONNECTED);
+ }
+
+ private void updateProfileStatus(LocalBluetoothProfile profile, int status) {
+ doReturn(status).when(profile).getConnectionStatus(mDevice);
+ mCachedDevice.onProfileStateChanged(profile, status);
+ }
}
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
new file mode 100644
index 0000000..9bb53ee
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.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.bluetooth;
+
+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 android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSap;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class SapProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothSap mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private SapProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new SapProfile(RuntimeEnvironment.application, mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothSap() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothSap() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
\ No newline at end of file
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 c23ad79..887c1d5 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
@@ -38,13 +38,13 @@
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
-import com.android.settingslib.testutils.FragmentTestUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.android.controller.ActivityController;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class LifecycleTest {
@@ -184,7 +184,7 @@
@Test
public void runThroughDialogFragmentLifecycles_shouldObserveEverything() {
final TestDialogFragment fragment = new TestDialogFragment();
- FragmentTestUtils.startFragment(fragment);
+ FragmentController.setupFragment(fragment);
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
@@ -208,7 +208,7 @@
@Test
public void runThroughFragmentLifecycles_shouldObserveEverything() {
final TestFragment fragment = new TestFragment();
- FragmentTestUtils.startFragment(fragment);
+ FragmentController.setupFragment(fragment);
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
@@ -248,7 +248,7 @@
@Test
public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() {
final TestFragment fragment = new TestFragment();
- FragmentTestUtils.startFragment(fragment);
+ FragmentController.setupFragment(fragment);
final OptionItemAccepter accepter = new OptionItemAccepter();
fragment.getLifecycle().addObserver(accepter);
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 96b2a14..b00476b2 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
@@ -50,7 +50,7 @@
+ "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+ "</licenses2>";
- private static final String EXPECTED_HTML_STRING =
+ private static final String HTML_HEAD_STRING =
"<html><head>\n"
+ "<style type=\"text/css\">\n"
+ "body { padding: 0; font-family: sans-serif; }\n"
@@ -63,8 +63,12 @@
+ "</head>"
+ "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
+ "<div class=\"toc\">\n"
- + "<ul>\n"
- + "<li><a href=\"#id0\">/file0</a></li>\n"
+ + "<ul>\n";
+
+ private static final String HTML_CUSTOM_HEADING = "Custom heading";
+
+ private static final String HTML_BODY_STRING =
+ "<li><a href=\"#id0\">/file0</a></li>\n"
+ "<li><a href=\"#id0\">/file1</a></li>\n"
+ "</ul>\n"
+ "</div><!-- table of contents -->\n"
@@ -81,6 +85,11 @@
+ "</td></tr><!-- same-license -->\n"
+ "</table></body></html>\n";
+ private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
+
+ private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING =
+ HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING;
+
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
@@ -117,7 +126,23 @@
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output));
+ fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), "");
assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
}
+
+ @Test
+ public void testGenerateHtmlWithCustomHeading() {
+ Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+ fileNameToContentIdMap.put("/file0", "0");
+ fileNameToContentIdMap.put("/file1", "0");
+ contentIdToFileContentMap.put("0", "license content #0");
+
+ StringWriter output = new StringWriter();
+ LicenseHtmlGeneratorFromXml.generateHtml(
+ fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output),
+ HTML_CUSTOM_HEADING);
+ assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING);
+ }
}
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 12a4e69..c32cc99 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
@@ -142,7 +142,7 @@
}
@Implementation
- static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+ static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
return sGenerateHtmlFileSucceeded;
}
}
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
new file mode 100644
index 0000000..3dc110d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -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.
+ */
+
+package com.android.settingslib.net;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+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;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleChartDataLoaderTest {
+
+ @Mock
+ private NetworkStatsManager mNetworkStatsManager;
+ @Mock
+ private Context mContext;
+
+ private NetworkCycleChartDataLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ }
+
+ @Test
+ public void recordUsage_shouldQueryNetworkSummary() throws RemoteException {
+ final long end = System.currentTimeMillis();
+ final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+ final int networkType = ConnectivityManager.TYPE_MOBILE;
+ final String subId = "TestSubscriber";
+ mLoader = NetworkCycleChartDataLoader.builder(mContext)
+ .setNetworkType(networkType).setSubscriberId(subId).build();
+
+ mLoader.recordUsage(start, end);
+
+ verify(mNetworkStatsManager).querySummary(networkType, subId, start, end);
+ }
+}
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
new file mode 100644
index 0000000..53fe451
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -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.
+ */
+
+package com.android.settingslib.net;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+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;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleDataForUidLoaderTest {
+
+ @Mock
+ private NetworkStatsManager mNetworkStatsManager;
+ @Mock
+ private Context mContext;
+
+ private NetworkCycleDataForUidLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ }
+
+ @Test
+ public void recordUsage_shouldQueryNetworkDetailsForUid() {
+ final long end = System.currentTimeMillis();
+ final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+ final int networkType = ConnectivityManager.TYPE_MOBILE;
+ final String subId = "TestSubscriber";
+ final int uid = 1;
+ mLoader = NetworkCycleDataForUidLoader.builder(mContext)
+ .setUid(uid).setNetworkType(networkType).setSubscriberId(subId).build();
+
+ mLoader.recordUsage(start, end);
+
+ verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+ }
+}
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
new file mode 100644
index 0000000..be7f1bb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkPolicy;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+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.util.ReflectionHelpers;
+
+import java.time.ZonedDateTime;
+import java.util.Iterator;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleDataLoaderTest {
+
+ @Mock
+ private NetworkStatsManager mNetworkStatsManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private NetworkPolicy mPolicy;
+ @Mock
+ private Iterator<Range<ZonedDateTime>> mIterator;
+ @Mock
+ private INetworkStatsService mNetworkStatsService;
+ @Mock
+ private NetworkCycleDataLoader.Builder mBuilder;
+
+
+ private NetworkCycleDataTestLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ when(mPolicy.cycleIterator()).thenReturn(mIterator);
+ }
+
+ @Test
+ public void loadInBackground_noNetworkPolicy_shouldLoad4WeeksData() {
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ doNothing().when(mLoader).loadFourWeeksData();
+
+ mLoader.loadInBackground();
+
+ verify(mLoader).loadFourWeeksData();
+ }
+
+ @Test
+ public void loadInBackground_hasNetworkPolicy_shouldLoadPolicyData() {
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
+
+ mLoader.loadInBackground();
+
+ verify(mLoader).loadPolicyData();
+ }
+
+ @Test
+ public void loadPolicyData_shouldRecordUsageFromPolicyCycle() {
+ final int networkType = ConnectivityManager.TYPE_MOBILE;
+ final String subId = "TestSubscriber";
+ final ZonedDateTime now = ZonedDateTime.now();
+ final Range<ZonedDateTime> cycle = new Range<>(now, now);
+ final long nowInMs = now.toInstant().toEpochMilli();
+ // mock 1 cycle data.
+ // hasNext() will be called internally in next(), hence setting it to return true twice.
+ when(mIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
+ when(mIterator.next()).thenReturn(cycle);
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
+ ReflectionHelpers.setField(mLoader, "mNetworkType", networkType);
+ ReflectionHelpers.setField(mLoader, "mSubId", subId);
+
+ mLoader.loadPolicyData();
+
+ verify(mLoader).recordUsage(nowInMs, nowInMs);
+ }
+
+ @Test
+ public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException {
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService);
+ final INetworkStatsSession networkSession = mock(INetworkStatsSession.class);
+ when(mNetworkStatsService.openSession()).thenReturn(networkSession);
+ final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class);
+ when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt()))
+ .thenReturn(networkHistory);
+ final long now = System.currentTimeMillis();
+ final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
+ when(networkHistory.getStart()).thenReturn(fourWeeksAgo);
+ when(networkHistory.getEnd()).thenReturn(now);
+
+ mLoader.loadFourWeeksData();
+
+ verify(mLoader).recordUsage(fourWeeksAgo, now);
+ }
+
+ public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
+
+ private NetworkCycleDataTestLoader(Context context) {
+ super(NetworkCycleDataLoader.builder(mContext));
+ mContext = context;
+ }
+
+ @Override
+ void recordUsage(long start, long end) {
+ }
+
+ @Override
+ List<NetworkCycleData> getCycleUsage() {
+ return null;
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
deleted file mode 100644
index d8e73b7..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.testutils;
-
-import android.os.Bundle;
-import android.widget.LinearLayout;
-
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-
-import org.robolectric.Robolectric;
-
-/**
- * Utilities for creating Fragments for testing.
- * <p>
- * TODO(b/111195449) - Duplicated from org.robolectric.shadows.support.v4.SupportFragmentTestUtil
- */
-@Deprecated
-public class FragmentTestUtils {
-
- public static void startFragment(Fragment fragment) {
- buildFragmentManager(FragmentUtilActivity.class)
- .beginTransaction().add(fragment, null).commit();
- }
-
- public static void startFragment(Fragment fragment,
- Class<? extends FragmentActivity> activityClass) {
- buildFragmentManager(activityClass)
- .beginTransaction().add(fragment, null).commit();
- }
-
- public static void startVisibleFragment(Fragment fragment) {
- buildFragmentManager(FragmentUtilActivity.class)
- .beginTransaction().add(1, fragment, null).commit();
- }
-
- public static void startVisibleFragment(Fragment fragment,
- Class<? extends FragmentActivity> activityClass, int containerViewId) {
- buildFragmentManager(activityClass)
- .beginTransaction().add(containerViewId, fragment, null).commit();
- }
-
- private static FragmentManager buildFragmentManager(
- Class<? extends FragmentActivity> activityClass) {
- FragmentActivity activity = Robolectric.setupActivity(activityClass);
- return activity.getSupportFragmentManager();
- }
-
- private static class FragmentUtilActivity extends FragmentActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- LinearLayout view = new LinearLayout(this);
- view.setId(1);
-
- setContentView(view);
- }
- }
-}
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
new file mode 100644
index 0000000..10c9dfb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget.apppreference;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.view.View;
+
+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.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class AppPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private AppPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.preference_app, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new AppPreference(mContext);
+ }
+
+ @Test
+ public void setProgress_showProgress() {
+ mPref.setProgress(1);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(android.R.id.progress).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setSummary_showSummaryContainer() {
+ mPref.setSummary("test");
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void noSummary_hideSummaryContainer() {
+ mPref.setSummary(null);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void foobar_testName() {
+ float iconSize = mContext.getResources().getDimension(R.dimen.secondary_app_icon_size);
+ assertThat(Float.floatToIntBits(iconSize)).isEqualTo(Float.floatToIntBits(32));
+ }
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c53417b..de86789 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -55,7 +55,7 @@
<bool name="def_networks_available_notification_on">true</bool>
<bool name="def_backup_enabled">false</bool>
- <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
+ <string name="def_backup_transport" translatable="false">com.android.localtransport/.LocalTransport</string>
<!-- Default value for whether or not to pulse the notification LED when there is a
pending notification -->
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index bd21b83..c09b763 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -659,6 +659,9 @@
dumpSetting(s, p,
Settings.Global.GPU_DEBUG_LAYERS,
GlobalSettingsProto.Gpu.DEBUG_LAYERS);
+ dumpSetting(s, p,
+ Settings.Global.ANGLE_ENABLED_APP,
+ GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
p.end(gpuToken);
final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 63978ba..18ec9c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -870,7 +870,11 @@
}
}
if (newRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
- != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) {
+ != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) ||
+ newRestrictions.getBoolean(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)
+ != prevRestrictions.getBoolean(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)) {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -3285,8 +3289,8 @@
if (currentVersion == 133) {
// Version 133: Add default end button behavior
final SettingsState systemSettings = getSystemSettingsLocked(userId);
- if (systemSettings.getSettingLocked(Settings.System.END_BUTTON_BEHAVIOR) ==
- null) {
+ if (systemSettings.getSettingLocked(Settings.System.END_BUTTON_BEHAVIOR)
+ .isNull()) {
String defaultEndButtonBehavior = Integer.toString(getContext()
.getResources().getInteger(R.integer.def_end_button_behavior));
systemSettings.insertSettingLocked(Settings.System.END_BUTTON_BEHAVIOR,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index da870bd..5c654b4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -151,6 +151,7 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
+ <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index ec35b3d..2530abc 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -44,6 +44,7 @@
import libcore.io.Streams;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.MetricsLogger;
@@ -234,6 +235,7 @@
*/
private boolean mTakingScreenshot;
+ @GuardedBy("sNotificationBundle")
private static final Bundle sNotificationBundle = new Bundle();
private boolean mIsWatch;
@@ -1059,10 +1061,12 @@
}
private static Notification.Builder newBaseNotification(Context context) {
- if (sNotificationBundle.isEmpty()) {
- // Rename notifcations from "Shell" to "Android System"
- sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- context.getString(com.android.internal.R.string.android_system_label));
+ synchronized (sNotificationBundle) {
+ if (sNotificationBundle.isEmpty()) {
+ // Rename notifcations from "Shell" to "Android System"
+ sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ context.getString(com.android.internal.R.string.android_system_label));
+ }
}
return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
.addExtras(sNotificationBundle)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 680f14b..0b1dab1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -38,11 +38,12 @@
public interface NotificationMenuRowPlugin extends Plugin {
public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW";
- public static final int VERSION = 4;
+ public static final int VERSION = 5;
@ProvidesInterface(version = OnMenuEventListener.VERSION)
public interface OnMenuEventListener {
public static final int VERSION = 1;
+
public void onMenuClicked(View row, int x, int y, MenuItem menu);
public void onMenuReset(View row);
@@ -53,6 +54,7 @@
@ProvidesInterface(version = MenuItem.VERSION)
public interface MenuItem {
public static final int VERSION = 1;
+
public View getMenuView();
public View getGutsView();
@@ -84,34 +86,136 @@
public void setMenuClickListener(OnMenuEventListener listener);
- public void setSwipeActionHelper(NotificationSwipeActionHelper listener);
-
public void setAppName(String appName);
public void createMenu(ViewGroup parent, StatusBarNotification sbn);
- public View getMenuView();
-
- public boolean isMenuVisible();
-
public void resetMenu();
- public void onTranslationUpdate(float translation);
+ public View getMenuView();
- public void onHeightUpdate();
+ /**
+ * Get the target position that a notification row should be snapped open to in order to reveal
+ * the menu. This is generally determined by the number of icons in the notification menu and the
+ * size of each icon. This method accounts for whether the menu appears on the left or ride side
+ * of the parent notification row.
+ *
- public void onNotificationUpdated(StatusBarNotification sbn);
+ * @return an int representing the x-offset in pixels that the notification should snap open to.
+ * Positive values imply that the notification should be offset to the right to reveal the menu,
+ * and negative alues imply that the notification should be offset to the right.
+ */
+ public int getMenuSnapTarget();
- public boolean onTouchEvent(View view, MotionEvent ev, float velocity);
+ /**
+ * Determines whether or not the menu should be shown in response to user input.
+ * @return true if the menu should be shown, false otherwise.
+ */
+ public boolean shouldShowMenu();
+
+ /**
+ * Determines whether the menu is currently visible.
+ * @return true if the menu is visible, false otherwise.
+ */
+ public boolean isMenuVisible();
+
+ /**
+ * Determines whether a given movement is towards or away from the current location of the menu.
+ * @param movement
+ * @return true if the movement is towards the menu, false otherwise.
+ */
+ public boolean isTowardsMenu(float movement);
+
+ /**
+ * Determines whether the menu should snap closed instead of dismissing the
+ * parent notification, as a function of its current state.
+ *
+ * @return true if the menu should snap closed, false otherwise.
+ */
+ public boolean shouldSnapBack();
+
+ /**
+ * Determines whether the menu was previously snapped open to the same side that it is currently
+ * being shown on.
+ * @return true if the menu is snapped open to the same side on which it currently appears,
+ * false otherwise.
+ */
+ public boolean isSnappedAndOnSameSide();
+
+ /**
+ * Determines whether the notification the menu is attached to is able to be dismissed.
+ * @return true if the menu's parent notification is dismissable, false otherwise.
+ */
+ public boolean canBeDismissed();
+
+ /**
+ * Determines whether the menu should remain open given its current state, or snap closed.
+ * @return true if the menu should remain open, false otherwise.
+ */
+ public boolean isWithinSnapMenuThreshold();
+
+ /**
+ * Determines whether the menu has been swiped far enough to snap open.
+ * @return true if the menu has been swiped far enough to open, false otherwise.
+ */
+ public boolean isSwipedEnoughToShowMenu();
public default boolean onInterceptTouchEvent(View view, MotionEvent ev) {
return false;
}
- public default boolean useDefaultMenuItems() {
+ public default boolean shouldUseDefaultMenuItems() {
return false;
}
- public default void onConfigurationChanged() {
- }
+ /**
+ * Callback used to signal the menu that its parent's translation has changed.
+ * @param translation The new x-translation of the menu as a position (not an offset).
+ */
+ public void onParentTranslationUpdate(float translation);
+
+ /**
+ * Callback used to signal the menu that its parent's height has changed.
+ */
+ public void onParentHeightUpdate();
+
+ /**
+ * Callback used to signal the menu that its parent notification has been updated.
+ * @param sbn
+ */
+ public void onNotificationUpdated(StatusBarNotification sbn);
+
+ /**
+ * Callback used to signal the menu that a user is moving the parent notification.
+ * @param delta The change in the parent notification's position.
+ */
+ public void onTouchMove(float delta);
+
+ /**
+ * Callback used to signal the menu that a user has begun touching its parent notification.
+ */
+ public void onTouchStart();
+
+ /**
+ * Callback used to signal the menu that a user has finished touching its parent notification.
+ */
+ public void onTouchEnd();
+
+ /**
+ * Callback used to signal the menu that it has been snapped closed.
+ */
+ public void onSnapClosed();
+
+ /**
+ * Callback used to signal the menu that it has been snapped open.
+ */
+ public void onSnapOpen();
+
+ /**
+ * Callback used to signal the menu that its parent notification has been dismissed.
+ */
+ public void onDismiss();
+
+ public default void onConfigurationChanged() { }
+
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
index f6cf035..8db0d02 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -39,7 +39,7 @@
/**
* Call this to snap a notification to provided {@code targetLeft}.
*/
- public void snap(View animView, float velocity, float targetLeft);
+ public void snapOpen(View animView, int targetLeft, float velocity);
/**
* Call this to snooze a notification based on the provided {@link SnoozeOption}.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index aa2fb32..814324e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -20,6 +20,7 @@
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import java.io.PrintWriter;
@ProvidesInterface(action = NavGesture.ACTION, version = NavGesture.VERSION)
public interface NavGesture extends Plugin {
@@ -46,6 +47,8 @@
public void onNavigationButtonLongPress(View v);
public default void destroy() { }
+
+ public default void dump(PrintWriter pw) { }
}
}
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index c892ac3..b64e0cf 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -22,8 +22,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="3171996292755059205">"የቁልፍ ጥበቃ"</string>
<string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"ፒን ኮድ ይተይቡ"</string>
- <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"የሲም ፒዩኬ እና አዲሱን ፒን ኮድ ይተይቡ"</string>
- <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"የሲም ፒዩኬ ኮድ"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"የሲም PUK እና አዲሱን ፒን ኮድ ይተይቡ"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"የሲም PUK ኮድ"</string>
<string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"አዲስ የሲም ፒን ኮድ"</string>
<string name="keyguard_password_entry_touch_hint" msgid="5790410752696806482"><font size="17">"የይለፍ ቃል ለመተየብ ይንኩ"</font></string>
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"ለመክፈት የይለፍ ቃል ይተይቡ"</string>
@@ -48,12 +48,12 @@
<string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"የማይሰራ ሲም ካርድ።"</string>
<string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"ሲም ካርድዎ እስከመጨረሻው ተሰናክሏል።\n ሌላ ሲም ካርድ ለማግኘት ከገመድ አልባ አገልግሎት አቅራቢዎ ጋር ይገናኙ።"</string>
<string name="keyguard_sim_locked_message" msgid="953766009432168127">"ሲም ካርድ ተዘግቷል።"</string>
- <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"ሲም ካርድ በፒዩኬ ተቆልፏል።"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"ሲም ካርድ በPUK ተቆልፏል።"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"ሲም ካርድን በመክፈት ላይ..."</string>
<string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"የፒን አካባቢ"</string>
<string name="keyguard_accessibility_password" msgid="7695303207740941101">"የመሣሪያ ይለፍ ቃል"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"የሲም ፒን አካባቢ"</string>
- <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"የሲም ፒዩኬ አካባቢ"</string>
+ <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"የሲም PUK አካባቢ"</string>
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"ቀጣዩ ማንቂያ ለ<xliff:g id="ALARM">%1$s</xliff:g> ተዘጋጅቷል"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ሰርዝ"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIMን አሰናክል"</string>
@@ -74,14 +74,14 @@
<string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> መሣሪያን ያለሞባይል አገልግሎት ለመጠቀም eSIMን ያሰናክሉ።"</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"ፒን ያስገቡ"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"የይለፍ ቃል ያስገቡ"</string>
- <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ሲም አሁን ተሰናክሏል። ለመቀጠል የፒዩኬ ኮድ ያስገቡ። ለዝርዝር አገልግሎት አቅራቢን ያግኙ።"</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"ሲም «<xliff:g id="CARRIER">%1$s</xliff:g>» አሁን ተሰናክሏል። ለመቀጠል የፒዩኬ ኮድ ያስገቡ። ዝርዝር መረጃን ለማግኘት የተንቀሳቃሽ ስልክ አገልግሎት አቅራቢውን ያነጋግሩ።"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ለዝርዝር አገልግሎት አቅራቢን ያግኙ።"</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"ሲም «<xliff:g id="CARRIER">%1$s</xliff:g>» አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ዝርዝር መረጃን ለማግኘት የተንቀሳቃሽ ስልክ አገልግሎት አቅራቢውን ያነጋግሩ።"</string>
<string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"የተፈለገውን የፒን ኮድ ያስገቡ"</string>
<string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"የተፈለገውን ፒን ኮድ ያረጋግጡ"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"ሲም ካርድን በመክፈት ላይ..."</string>
<string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"ከ4 እስከ 8 ቁጥሮች የያዘ ፒን ይተይቡ።"</string>
- <string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"የፒዩኬ ኮድ 8 ወይም ከዚያ በላይ ቁጥሮች ሊኖረው ይገባል።"</string>
- <string name="kg_invalid_puk" msgid="5399287873762592502">"ትክክለኛውን የፒዩኬ ኮድ እንደገና ያስገቡ። ተደጋጋሚ ሙከራዎች ሲሙን እስከመጨረሻው ያሰናክሉታል።"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"የPUK ኮድ 8 ወይም ከዚያ በላይ ቁጥሮች ሊኖረው ይገባል።"</string>
+ <string name="kg_invalid_puk" msgid="5399287873762592502">"ትክክለኛውን የPUK ኮድ እንደገና ያስገቡ። ተደጋጋሚ ሙከራዎች ሲሙን እስከመጨረሻው ያሰናክሉታል።"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"ፒን ኮዶቹ አይገጣጠሙም"</string>
<string name="kg_login_too_many_attempts" msgid="6604574268387867255">"በጣም ብዙ የስርዓተ ጥለት ሙከራዎች"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"ፒንዎን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልተየቡም። \n\nበ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
@@ -112,7 +112,7 @@
<item quantity="other">ልክ ያልሆነ የሲም ፒዩኬ ኮድ፣ ሲሙ እስከመጨረሻው የማይሰራ ከመሆኑ በፊት <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
</plurals>
<string name="kg_password_pin_failed" msgid="8769990811451236223">"የሲም ፒን ክወና አልተሳካም!"</string>
- <string name="kg_password_puk_failed" msgid="1331621440873439974">"የሲም ፒዩኬ ክወና አልተሳካም!"</string>
+ <string name="kg_password_puk_failed" msgid="1331621440873439974">"የሲም PUK ክወና አልተሳካም!"</string>
<string name="kg_pin_accepted" msgid="7637293533973802143">"ኮዱ ተቀባይነት አግኝቷል!"</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"ከአገልግሎት መስጫ ክልል ውጪ።"</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"የግቤት ስልት ቀይር"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index b3b9732..50dc26b 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -117,13 +117,13 @@
<string name="keyguard_carrier_default" msgid="4274828292998453695">"Ingen dækning."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"Skift indtastningsmetode"</string>
<string name="airplane_mode" msgid="3807209033737676010">"Flytilstand"</string>
- <string name="kg_prompt_reason_restart_pattern" msgid="7246972020562621506">"Der skal angives et mønster efter genstart af enheden"</string>
+ <string name="kg_prompt_reason_restart_pattern" msgid="7246972020562621506">"Du skal angive et mønster, når du har genstartet enheden"</string>
<string name="kg_prompt_reason_restart_pin" msgid="6303592361322290145">"Der skal indtastes en pinkode efter genstart af enheden"</string>
<string name="kg_prompt_reason_restart_password" msgid="6984641181515902406">"Der skal indtastes en adgangskode efter genstart af enheden"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="5304487696073914063">"Der kræves et mønster som ekstra beskyttelse"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="8851462864335757813">"Der kræves en pinkode som ekstra beskyttelse"</string>
<string name="kg_prompt_reason_timeout_password" msgid="6563904839641583441">"Der kræves en adgangskode som ekstra beskyttelse"</string>
- <string name="kg_prompt_reason_switch_profiles_pattern" msgid="3398054847288438444">"Der skal angives et mønster, når du skifter profil"</string>
+ <string name="kg_prompt_reason_switch_profiles_pattern" msgid="3398054847288438444">"Du skal angive et mønster, når du skifter profil"</string>
<string name="kg_prompt_reason_switch_profiles_pin" msgid="7426368139226961699">"Der skal indtastes en pinkode, når du skifter profil"</string>
<string name="kg_prompt_reason_switch_profiles_password" msgid="8383831046318421845">"Der skal indtastes en adgangskode, når du skifter profil"</string>
<string name="kg_prompt_reason_device_admin" msgid="3452168247888906179">"Enheden er blevet låst af administratoren"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index d5e36ce..f0c7e92 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -34,9 +34,9 @@
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Le code est incorrect."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Carte non valide."</string>
<string name="keyguard_charged" msgid="3316115607283493413">"Complètement chargée"</string>
- <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rechargement…"</string>
- <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rechargement rapide…"</string>
- <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rechargement lent…"</string>
+ <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge…"</string>
+ <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge rapide…"</string>
+ <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente…"</string>
<string name="keyguard_low_battery" msgid="9218432555787624490">"Branchez votre chargeur."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"Appuyez sur \"Menu\" pour déverrouiller le clavier."</string>
<string name="keyguard_network_locked_message" msgid="6743537524631420759">"Réseau verrouillé"</string>
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
similarity index 80%
rename from packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
rename to packages/SystemUI/res/drawable/biometric_dialog_bg.xml
index 221f170..335448d 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
@@ -17,10 +17,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/fingerprint_dialog_bg_color" />
+ <solid android:color="@color/biometric_dialog_bg_color" />
<corners android:radius="1dp"
- android:topLeftRadius="@dimen/fingerprint_dialog_corner_size"
- android:topRightRadius="@dimen/fingerprint_dialog_corner_size"
+ android:topLeftRadius="@dimen/biometric_dialog_corner_size"
+ android:topRightRadius="@dimen/biometric_dialog_corner_size"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/>
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_icon.xml b/packages/SystemUI/res/drawable/face_dialog_icon.xml
new file mode 100644
index 0000000..6d28b5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_icon.xml
@@ -0,0 +1,24 @@
+<?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:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index 83c1949..05fd467 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -36,7 +36,7 @@
android:name="_R_G_L_2_G_D_0_P_0"
android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -47,7 +47,7 @@
android:name="_R_G_L_2_G_D_1_P_0"
android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -58,7 +58,7 @@
android:name="_R_G_L_2_G_D_2_P_0"
android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -69,7 +69,7 @@
android:name="_R_G_L_2_G_D_3_P_0"
android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -80,7 +80,7 @@
android:name="_R_G_L_2_G_D_4_P_0"
android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -97,7 +97,7 @@
android:name="_R_G_L_1_G_D_0_P_0"
android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="2"
@@ -118,7 +118,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
</group>
@@ -131,7 +131,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_1"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
</group>
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index f682f87..fd0ab22 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -36,7 +36,7 @@
android:name="_R_G_L_3_G_D_0_P_0"
android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -47,7 +47,7 @@
android:name="_R_G_L_3_G_D_1_P_0"
android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -58,7 +58,7 @@
android:name="_R_G_L_3_G_D_2_P_0"
android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -69,7 +69,7 @@
android:name="_R_G_L_3_G_D_3_P_0"
android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -80,7 +80,7 @@
android:name="_R_G_L_3_G_D_4_P_0"
android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -101,7 +101,7 @@
android:name="_R_G_L_2_G_D_0_P_0"
android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -112,7 +112,7 @@
android:name="_R_G_L_2_G_D_1_P_0"
android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -123,7 +123,7 @@
android:name="_R_G_L_2_G_D_2_P_0"
android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -134,7 +134,7 @@
android:name="_R_G_L_2_G_D_3_P_0"
android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -145,7 +145,7 @@
android:name="_R_G_L_2_G_D_4_P_0"
android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -162,7 +162,7 @@
android:name="_R_G_L_1_G_D_0_P_0"
android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="2"
@@ -183,7 +183,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
</group>
@@ -196,7 +196,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_1"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
</group>
diff --git a/packages/SystemUI/res/layout/fingerprint_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
similarity index 78%
rename from packages/SystemUI/res/layout/fingerprint_dialog.xml
rename to packages/SystemUI/res/layout/biometric_dialog.xml
index 1bdaf6e..0417e2e 100644
--- a/packages/SystemUI/res/layout/fingerprint_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -19,7 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
- android:background="@color/fingerprint_dialog_dim_color"
+ android:background="@color/biometric_dialog_dim_color"
android:orientation="vertical">
<!-- This is not a Space since Spaces cannot be clicked -->
@@ -47,7 +47,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:elevation="2dp"
- android:background="@drawable/fingerprint_dialog_bg">
+ android:background="@drawable/biometric_dialog_bg">
<TextView
android:id="@+id/title"
@@ -57,13 +57,13 @@
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
- android:gravity="@integer/fingerprint_dialog_text_gravity"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:textSize="20sp"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
- android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+ android:textColor="@color/biometric_dialog_text_dark_color"/>
<TextView
android:id="@+id/subtitle"
@@ -72,13 +72,13 @@
android:layout_marginTop="8dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
- android:gravity="@integer/fingerprint_dialog_text_gravity"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:textSize="16sp"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
- android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+ android:textColor="@color/biometric_dialog_text_dark_color"/>
<TextView
android:id="@+id/description"
@@ -86,20 +86,19 @@
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
- android:gravity="@integer/fingerprint_dialog_text_gravity"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:paddingTop="8dp"
android:textSize="16sp"
android:maxLines="4"
- android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+ android:textColor="@color/biometric_dialog_text_dark_color"/>
<ImageView
- android:id="@+id/fingerprint_icon"
- android:layout_width="@dimen/fingerprint_dialog_fp_icon_size"
- android:layout_height="@dimen/fingerprint_dialog_fp_icon_size"
+ android:id="@+id/biometric_icon"
+ android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
+ android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
android:layout_gravity="center_horizontal"
android:layout_marginTop="48dp"
- android:scaleType="fitXY"
- android:contentDescription="@string/accessibility_fingerprint_dialog_fingerprint_icon" />
+ android:scaleType="fitXY" />
<TextView
android:id="@+id/error"
@@ -112,9 +111,8 @@
android:textSize="12sp"
android:gravity="center_horizontal"
android:accessibilityLiveRegion="polite"
- android:text="@string/fingerprint_dialog_touch_sensor"
- android:contentDescription="@string/accessibility_fingerprint_dialog_help_area"
- android:textColor="@color/fingerprint_dialog_text_light_color"/>
+ android:contentDescription="@string/accessibility_biometric_dialog_help_area"
+ android:textColor="@color/biometric_dialog_text_light_color"/>
<LinearLayout
android:layout_width="match_parent"
@@ -125,7 +123,7 @@
android:orientation="horizontal"
android:measureWithLargestChild="true">
<Space android:id="@+id/leftSpacer"
- android:layout_width="24dp"
+ android:layout_width="12dp"
android:layout_height="match_parent"
android:visibility="visible" />
<!-- Negative Button -->
@@ -133,20 +131,26 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_marginStart="-12dp"
- android:gravity="start|center_vertical"
+ android:gravity="center"
android:maxLines="2" />
+ <Space android:id="@+id/middleSpacer"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="visible" />
<!-- Positive Button -->
<Button android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_marginEnd="12dp"
- android:maxLines="2" />
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_confirm"
+ android:visibility="gone"/>
<Space android:id="@+id/rightSpacer"
- android:layout_width="24dip"
+ android:layout_width="12dip"
android:layout_height="match_parent"
- android:visibility="gone" />
+ android:visibility="visible" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index bb67c54..87b4551 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -17,6 +17,7 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">->
<View
+ android:id="@+id/customizer_transparent_view"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:background="@android:color/transparent" />
diff --git a/packages/SystemUI/res/layout/qs_detail_buttons.xml b/packages/SystemUI/res/layout/qs_detail_buttons.xml
index 03ed62b..75f43f9 100644
--- a/packages/SystemUI/res/layout/qs_detail_buttons.xml
+++ b/packages/SystemUI/res/layout/qs_detail_buttons.xml
@@ -26,6 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
+ android:minHeight="48dp"
android:minWidth="132dp"
android:textAppearance="@style/TextAppearance.QS.DetailButton"
android:focusable="true" />
@@ -35,6 +36,7 @@
style="@style/QSBorderlessButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:minWidth="88dp"
android:textAppearance="@style/TextAppearance.QS.DetailButton"
android:focusable="true"/>
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index 07f0c83..a8960d9 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_page"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:paddingStart="@dimen/notification_side_paddings"
android:paddingEnd="@dimen/notification_side_paddings"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index e96a09b..7083269 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -19,10 +19,14 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
+ android:layout_weight="1"
+ android:clipChildren="true"
+ android:clipToPadding="true"
+ android:paddingStart="@dimen/notification_side_paddings"
+ android:paddingEnd="@dimen/notification_side_paddings"
android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
+
<FrameLayout
android:id="@+id/page_decor"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index adf1e74..2538612 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -37,7 +37,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
- android:textColor="@android:color/white"
+ android:textColor="?attr/wallpaperTextColor"
android:textSize="16sp"/>
<ImageView
android:id="@+id/dismiss"
@@ -49,6 +49,7 @@
android:layout_marginEnd="2dp"
android:alpha="0.7"
android:src="@drawable/ic_close_white"
+ android:tint="?attr/wallpaperTextColor"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/accessibility_desc_close"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 44691c2..157934f 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"maak kamera oop"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Kies nuwe taakuitleg"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Hulpboodskapgebied"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestig"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Programikoon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Hulpboodskapgebied"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Soek tans vir jou …"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Gesig-ikoon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth gekoppel."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Berging"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Wenke"</string>
<string name="instant_apps" msgid="6647570248119804907">"Kitsprogramme"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Kitsprogramme hoef nie geïnstalleer te word nie."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> loop tans"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Program is oopgemaak sonder dat dit geïnstalleer is."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Program is oopgemaak sonder dat dit geïnstalleer is. Tik om meer te wete te kom."</string>
<string name="app_info" msgid="6856026610594615344">"Programinligting"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Gaan na blaaier"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Gaan na web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiele data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is af"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 966e4d7..492fa8c 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ካሜራ ክፈት"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"የአዲስ ተግባር አቀማመጥን ይምረጡ"</string>
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"የእገዛ መልዕክት አካባቢ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"አረጋግጥ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"የጣት አሻራ ዳሳሹን ይንኩ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"የጣት አሻራ አዶ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"የመተግበሪያ አዶ"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"የእገዛ መልዕክት አካባቢ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"እርስዎን በመፈለግ ላይ…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"የፊት አዶ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ብሉቱዝ ተያይዟል።"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ማከማቻ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ፍንጮች"</string>
<string name="instant_apps" msgid="6647570248119804907">"የቅጽበት መተግበሪያዎች"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ቅጽበታዊ መተግበሪያዎች መጫን አያስፈልጋቸውም።"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> አሂድ"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"መተግበሪያ ሳይጫን ተከፍቷል።"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"መተግበሪያ ሳይጫን ተከፍቷል። ተጨማሪ ለማወቅ መታ ያድርጉ።"</string>
<string name="app_info" msgid="6856026610594615344">"የመተግበሪያ መረጃ"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ወደ አሳሽ ሂድ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ወደ ድር ሂድ"</string>
<string name="mobile_data" msgid="7094582042819250762">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g>— <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ጠፍቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 278b00a..fe27bb7 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -107,10 +107,12 @@
<string name="camera_label" msgid="7261107956054836961">"فتح الكاميرا"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"تحديد تنسيق جديد للمهمة"</string>
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"منطقة رسالة المساعدة"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأكيد"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"المس جهاز استشعار بصمات الإصبع"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"رمز بصمة الإصبع"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"رمز التطبيق"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"منطقة رسالة المساعدة"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"جارٍ البحث عن وجهك…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"رمز الوجه"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"زر تكبير/تصغير للتوافق."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"استخدام التكبير/التصغير لتحويل شاشة صغيرة إلى شاشة أكبر"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"تم توصيل البلوتوث."</string>
@@ -847,9 +849,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"سعة التخزين"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"تلميحات"</string>
<string name="instant_apps" msgid="6647570248119804907">"التطبيقات الفورية"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"لا تتطلب التطبيقات الفورية إجراء التثبيت."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"التطبيق <xliff:g id="APP">%1$s</xliff:g> قيد التشغيل"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"تمّ فتح التطبيق بدون تثبيته."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"تمّ فتح التطبيق بدون تثبيته. انقر لمعرفة مزيد من المعلومات."</string>
<string name="app_info" msgid="6856026610594615344">"معلومات عن التطبيق"</string>
- <string name="go_to_web" msgid="2650669128861626071">"الانتقال إلى المتصفح"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"الانتقال إلى الويب"</string>
<string name="mobile_data" msgid="7094582042819250762">"بيانات الجوّال"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"تم إيقاف شبكة Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8912f15..8cde524 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"কেমেৰা খোলক"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কাৰ্যৰ চানেকি বাছনি কৰক"</string>
<string name="cancel" msgid="6442560571259935130">"বাতিল কৰক"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"সহায় বাৰ্তাৰ ক্ষেত্ৰ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"নিশ্চিত কৰক"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"এপ্লিকেশ্বন আইকন"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"সহায় বাৰ্তাৰ ক্ষেত্ৰ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপোনাৰ মুখমণ্ডল বিচাৰি আছে…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"মুখমণ্ডলৰ আইকন"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"উপযোগিতা অনুসৰি জুম কৰা বুটাম।"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"স্ক্ৰীণৰ আকাৰ ডাঙৰ কৰিবলৈ জুম কৰক।"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ব্লুটুথ সংযোগ হ\'ল।"</string>
@@ -344,7 +346,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সীমা"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সকীয়নি"</string>
<string name="quick_settings_work_mode_label" msgid="7608026833638817218">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
- <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ৰাতিৰ লাইট"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ৰাতিৰ পোহৰ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"সূৰ্যাস্তত অন কৰক"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"সূৰ্যোদয়ৰ লৈকে"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"<xliff:g id="TIME">%s</xliff:g>ত অন কৰক"</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"সঞ্চয়াগাৰ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ইংগিতবোৰ"</string>
<string name="instant_apps" msgid="6647570248119804907">"তাৎক্ষণিক এপসমূহ"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"তাৎক্ষণিক এপসমূহক ইনষ্টল কৰাৰ প্ৰয়োজন নাই।"</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"এপ্ সম্পৰ্কীয় তথ্য"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ব্ৰাউজাৰলৈ যাওক"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ৱেবলৈ যাওক"</string>
<string name="mobile_data" msgid="7094582042819250762">"ম\'বাইল ডেটা"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ৱাই-ফাই অফ অৱস্থাত আছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index f0779fb..2342a89 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"kemaranı açın"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Yeni tapşırıq sxemi seçin"</string>
<string name="cancel" msgid="6442560571259935130">"Ləğv et"</string>
+ <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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Tətbiq ikonası"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Yardım mesajı bölməsi"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Siz axtarılırsınız…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Üz işarəsi"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyğunluq zoom düyməsi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha böyük ekranda uzaqlaşdır."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth qoşulub."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Yaddaş"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Məsləhətlər"</string>
<string name="instant_apps" msgid="6647570248119804907">"Ani Tətbiqlər"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Ani tətbiqlər quraşdırma tələb etmir."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> işləyir"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Quraşdırılmadan açılan tətbiq."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Quraşdırılmadan açılan tətbiq. Ətraflı məlumat üçün klikləyin."</string>
<string name="app_info" msgid="6856026610594615344">"Tətbiq məlumatı"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Brauzerə daxil edin"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Vebə keçin"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobil data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi deaktivdir"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 9dc6460..47422b2 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -104,10 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Izaberi novi raspored zadataka"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikacije"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Oblast poruke za pomoć"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona lica"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Dugme Zum kompatibilnosti."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zumiranje sa manjeg na veći ekran."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth je priključen."</string>
@@ -829,9 +831,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Memorijski prostor"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Saveti"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant aplikacije"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant aplikacije ne zahtevaju instalaciju."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikacija se otvorila bez instaliranja."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikacija se otvorila bez instaliranja. Dodirnite da biste saznali više."</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Idi na pregledač"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Idi na veb"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je isključen"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 9898dbd..de01516 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"адкрыць камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Выберыце новы макет заданняў"</string>
<string name="cancel" msgid="6442560571259935130">"Скасаваць"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Поле даведачнага паведамлення"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Пацвердзіць"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Дакраніцеся да сканера адбіткаў пальцаў"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок адбіткаў пальцаў"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Значок праграмы"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Вобласць даведачнага паведамлення"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ідзе пошук вашага твару…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Значок твару"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка сумяшчальнасці маштаба."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Маштабаванне малых элементаў для большага экрана."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-сувязь."</string>
@@ -837,9 +839,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Захоўванне"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Падказкі"</string>
<string name="instant_apps" msgid="6647570248119804907">"Імгненныя праграмы"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Імгненныя праграмы не патрабуюць усталёўкі."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Праграма \"<xliff:g id="APP">%1$s</xliff:g>\" запушчана"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Праграма адкрыта без усталёўкі."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Праграма адкрыта без усталёўкі. Націсніце, каб даведацца больш."</string>
<string name="app_info" msgid="6856026610594615344">"Інфармацыя пра праграму"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Перайсці ў браўзер"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Перайсці ў інтэрнэт"</string>
<string name="mobile_data" msgid="7094582042819250762">"Маб. перадача даных"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi выключаны"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index a5506a3..29d89ec 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"отваряне на камерата"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Избиране на ново оформление за задачите"</string>
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област за помощно съобщение"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потвърждаване"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Докоснете сензора за отпечатъци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатък"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Икона на приложението"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Област за помощно съобщение"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Търсим ви…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Икона на лице"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth е включен."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Хранилище"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Съвети"</string>
<string name="instant_apps" msgid="6647570248119804907">"Мигновени приложения"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"За мигновените приложения не се изисква инсталиране."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> работи"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Приложението се отвори, без да бъде инсталирано."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Приложението се отвори, без да бъде инсталирано. Докоснете, за да научите повече."</string>
<string name="app_info" msgid="6856026610594615344">"Информация за приложението"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Към браузъра"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Към мрежата"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилни данни"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Функцията за Wi‑Fi е изключена"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5d52103..d2f6401 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -31,7 +31,7 @@
</plurals>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"কোনো বিজ্ঞপ্তি নেই"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"চলতে-থাকা"</string>
- <string name="status_bar_latest_events_title" msgid="6594767438577593172">"বিজ্ঞপ্তিগুলি"</string>
+ <string name="status_bar_latest_events_title" msgid="6594767438577593172">"বিজ্ঞপ্তি"</string>
<string name="battery_low_title" msgid="9187898087363540349">"চার্জ শীঘ্রই শেষ হয়ে যেতে পারে"</string>
<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>
@@ -49,7 +49,7 @@
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"অটো-রোটেট স্ক্রিন"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"মিউট করুন"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"স্বতঃ"</string>
- <string name="status_bar_settings_notifications" msgid="397146176280905137">"বিজ্ঞপ্তিগুলি"</string>
+ <string name="status_bar_settings_notifications" msgid="397146176280905137">"বিজ্ঞপ্তি"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"ব্লুটুথ টিথার করা হয়েছে"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ইনপুট পদ্ধতিগুলি সেট আপ করুন"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ফিজিক্যাল কীবোর্ড"</string>
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ক্যামেরা খুলুন"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কার্য লেআউট বেছে নিন"</string>
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"সহায়তার মেসেজ দেখানোর জায়গা"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"কনফার্ম করুন"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"আঙ্গুলের ছাপের সেন্সর স্পর্শ করুন"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"আঙ্গুলের ছাপের আইকন"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"অ্যাপ্লিকেশনের আইকন"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"সহায়তার মেসেজ দেখানোর জায়গা"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপনার জন্য খোঁজা হচ্ছে…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ফেস আইকন"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"সামঞ্জস্যের জুম বোতাম৷"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ছোট থেকে বৃহৎ স্ক্রীণে জুম করুন৷"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ব্লুটুথ সংযুক্ত হয়েছে৷"</string>
@@ -174,7 +176,7 @@
<string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ ব্যাটারি রয়েছে৷"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ব্যাটারি চার্জ হচ্ছে, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ৷"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"সিস্টেম সেটিংস৷"</string>
- <string name="accessibility_notifications_button" msgid="4498000369779421892">"বিজ্ঞপ্তিগুলি৷"</string>
+ <string name="accessibility_notifications_button" msgid="4498000369779421892">"বিজ্ঞপ্তি৷"</string>
<string name="accessibility_overflow_action" msgid="5681882033274783311">"সমস্ত বিজ্ঞপ্তি দেখুন"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"বিজ্ঞপ্তি সাফ করুন৷"</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"GPS সক্ষম করা হয়েছে৷"</string>
@@ -334,7 +336,7 @@
<item quantity="one">%dটি ডিভাইস</item>
<item quantity="other">%dটি ডিভাইস</item>
</plurals>
- <string name="quick_settings_notifications_label" msgid="4818156442169154523">"বিজ্ঞপ্তিগুলি"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"বিজ্ঞপ্তি"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ফ্ল্যাশলাইট"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"মোবাইল ডেটা"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ডেটার ব্যবহার"</string>
@@ -576,7 +578,7 @@
<string name="broadcast_tile" msgid="3894036511763289383">"সম্প্রচার টাইল"</string>
<string name="zen_alarm_warning_indef" msgid="3482966345578319605">"তার আগে আপনি এটিকে বন্ধ না করা পর্যন্ত আপনি পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
<string name="zen_alarm_warning" msgid="444533119582244293">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
- <string name="alarm_template" msgid="3980063409350522735">"<xliff:g id="WHEN">%1$s</xliff:g> -টায়"</string>
+ <string name="alarm_template" msgid="3980063409350522735">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="4242179982586714810">"<xliff:g id="WHEN">%1$s</xliff:g> -তে"</string>
<string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"দ্রুত সেটিংস, <xliff:g id="TITLE">%s</xliff:g>৷"</string>
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"হটস্পট"</string>
@@ -679,7 +681,7 @@
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"হোম"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"সাম্প্রতিকগুলি"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"পিছনে"</string>
- <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"বিজ্ঞপ্তিগুলি"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"বিজ্ঞপ্তি"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"কীবোর্ড শর্টকাট"</string>
<string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"কীবোর্ড লে-আউট পাল্টান"</string>
<string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"অ্যাপ্লিকেশানগুলি"</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"স্টোরেজ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"হিন্ট"</string>
<string name="instant_apps" msgid="6647570248119804907">"ঝটপট অ্যাপ"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ঝটপট অ্যাপ ইনস্টল করার প্রয়োজন হয় না।"</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"অ্যাপের তথ্য"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ব্রাউজারে যান"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ওয়েবে যান"</string>
<string name="mobile_data" msgid="7094582042819250762">"মোবাইল ডেটা"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ওয়াই ফাই বন্ধ আছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 4527e9a..ac0c05e 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -104,10 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi raspored zadataka"</string>
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Prostor za poruku za pomoć"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdite"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikacije"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Prostor za poruku za pomoć"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona lica"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Dugme za uvećavanje u slučaju nekompatibilnosti."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Uvećani prikaz manjeg ekrana na većem ekranu."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth je povezan."</string>
@@ -831,9 +833,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Pohrana"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Savjeti"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant-aplikacije"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Za instant aplikacije nije potrebna instalacija"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Pokrenuta je aplikacija <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikacija je otvorena bez prethodne instalacije."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikacija je otvorena bez prethodne instalacije. Dodirnite da saznate više."</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Idi na preglednik"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Idite na internet"</string>
<string name="mobile_data" msgid="7094582042819250762">"Prijenos podataka"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WiFi veza je isključena"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 23754ea..b4a2f3b 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"obre la càmera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el disseny de la tasca nova"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Àrea de missatge d\'ajuda"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirma"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icona d\'aplicació"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Àrea de missatge d\'ajuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"S\'està cercant la teva cara…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icona facial"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connectat."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Emmagatzematge"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Suggeriments"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplicacions instantànies"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"No cal instal·lar les aplicacions instantànies."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"S\'està executant <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"L\'aplicació s\'ha obert sense instal·lar-se."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"L\'aplicació s\'ha obert sense instal·lar-se. Toca per obtenir més informació."</string>
<string name="app_info" msgid="6856026610594615344">"Informació de l\'aplicació"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ves al navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Ves al web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dades mòbils"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"La Wi-Fi està desactivada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index e7583b5..6d4af4a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"spustit fotoaparát"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Vybrat nové rozvržení úkolů"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
+ <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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikace"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Oblast pro zprávu nápovědy"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hledáme vás…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona obličeje"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Rozhraní Bluetooth je připojeno."</string>
@@ -837,9 +839,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Úložiště"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Tipy"</string>
<string name="instant_apps" msgid="6647570248119804907">"Okamžité aplikace"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Okamžité aplikace není třeba instalovat."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Aplikace <xliff:g id="APP">%1$s</xliff:g> je spuštěna"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikace byla otevřena bez instalace."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikace byla otevřena bez instalace. Klepnutím zobrazíte další informace."</string>
<string name="app_info" msgid="6856026610594615344">"O aplikaci"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Přejít do prohlížeče"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Přejít na web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilní data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je vypnuta"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 31634a9..ade7db1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"åbn kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Vælg nyt opgavelayout"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Appens ikon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Område med hjælpemeddelelse"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Forsøger at finde dig…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ansigt"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knap for kompatibilitetszoom."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom mindre til større skærm."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth tilsluttet."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Lagerplads"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Tips"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps kræver ingen installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> kører"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"En app blev åbnet uden at blive installeret."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"En app blev åbnet uden at blive installeret. Tryk for at få flere oplysninger."</string>
<string name="app_info" msgid="6856026610594615344">"Appinfo"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Gå til en browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Gå til website"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobildata"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi er slået fra"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index aa6e449..06aac5c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"Kamera öffnen"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Neues Aufgabenlayout auswählen"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"App-Symbol"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Bereich für die Hilfemeldung"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Wir suchen nach dir…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Gesichtssymbol"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Schaltfläche für Kompatibilitätszoom"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom auf einen größeren Bildschirm"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Mit Bluetooth verbunden"</string>
@@ -448,9 +450,9 @@
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Benachrichtigungen durch \"Bitte nicht stören\" pausiert"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Jetzt starten"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Keine Benachrichtigungen"</string>
- <string name="profile_owned_footer" msgid="8021888108553696069">"Profil wird möglicherweise überwacht."</string>
- <string name="vpn_footer" msgid="2388611096129106812">"Das Netzwerk wird möglicherweise überwacht."</string>
- <string name="branded_vpn_footer" msgid="2168111859226496230">"Das Netzwerk wird möglicherweise überwacht"</string>
+ <string name="profile_owned_footer" msgid="8021888108553696069">"Profil wird eventuell überwacht."</string>
+ <string name="vpn_footer" msgid="2388611096129106812">"Das Netzwerk wird eventuell überwacht."</string>
+ <string name="branded_vpn_footer" msgid="2168111859226496230">"Das Netzwerk wird eventuell überwacht"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="6645176135063957394">"Deine Organisation verwaltet dieses Gerät und kann den Netzwerkverkehr überwachen"</string>
<string name="quick_settings_disclosure_named_management_monitoring" msgid="370622174777570853">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> verwaltet dieses Gerät und kann den Netzwerkverkehr überwachen"</string>
<string name="quick_settings_disclosure_management_named_vpn" msgid="1085137869053332307">"Das Gerät wird von deiner Organisation verwaltet und ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden"</string>
@@ -461,7 +463,7 @@
<string name="quick_settings_disclosure_named_management_vpns" msgid="7777821385318891527">"Das Gerät wird von <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> verwaltet und ist mit VPNs verbunden"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="5125463987558278215">"Deine Organisation kann den Netzwerkverkehr in deinem Arbeitsprofil überwachen"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8973606847896650284">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kann den Netzwerkverkehr in deinem Arbeitsprofil überwachen"</string>
- <string name="quick_settings_disclosure_monitoring" msgid="679658227269205728">"Das Netzwerk wird möglicherweise überwacht"</string>
+ <string name="quick_settings_disclosure_monitoring" msgid="679658227269205728">"Das Netzwerk wird eventuell überwacht"</string>
<string name="quick_settings_disclosure_vpns" msgid="8170318392053156330">"Das Gerät ist mit VPNs verbunden"</string>
<string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="3494535754792751741">"Arbeitsprofil verbunden mit <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="4467456202486569906">"Das persönliche Profil ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden"</string>
@@ -827,9 +829,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Speicher"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hinweise"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant-Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Bei Instant-Apps ist keine vorherige Installation erforderlich."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> wird ausgeführt"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App wurde geöffnet, ohne vorher installiert zu werden."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App wurde geöffnet, ohne vorher installiert zu werden. Tippe, um weitere Informationen zu erhalten."</string>
<string name="app_info" msgid="6856026610594615344">"App-Informationen"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Browser öffnen"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Web aufrufen"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile Daten"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WLAN ist deaktiviert"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 1a1c762..1892da6 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"άνοιγμα φωτογραφικής μηχανής"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Επιλέξτε τη νέα διάταξη εργασίας"</string>
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Περιοχή μηνυμάτων βοήθειας"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Επιβεβαίωση"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Εικονίδιο εφαρμογής"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Περιοχή μηνυμάτων βοήθειας"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Αναζήτηση για εσάς…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Εικονίδιο προσώπου"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Κουμπί εστίασης συμβατότητας."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Ζουμ από μικρότερη σε μεγαλύτερη οθόνη."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Το Bluetooth είναι συνδεδεμένο."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Αποθηκευτικός χώρος"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Συμβουλές"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Εφαρμογές"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Οι Instant Εφαρμογές δεν απαιτούν εγκατάσταση."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Η εφαρμογή άνοιξε χωρίς να έχει εγκατασταθεί."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Η εφαρμογή άνοιξε χωρίς να έχει εγκατασταθεί. Πατήστε για να μάθετε περισσότερα."</string>
<string name="app_info" msgid="6856026610594615344">"Πληροφορίες εφαρμογής"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Μετάβ. σε πρόγ. περ."</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Μετάβαση στον ιστό"</string>
<string name="mobile_data" msgid="7094582042819250762">"Δεδομένα κινητής τηλεφωνίας"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Το Wi-Fi είναι ανενεργό"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index c0afae6..6503470 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Face icon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hints"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App opened without being installed."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App opened without being installed. Tap to find out more."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 488c748..9e4081c 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Face icon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hints"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App opened without being installed."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App opened without being installed. Tap to find out more."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index c0afae6..6503470 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Face icon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hints"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App opened without being installed."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App opened without being installed. Tap to find out more."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index c0afae6..6503470 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Face icon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hints"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App opened without being installed."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App opened without being installed. Tap to find out more."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index f775bb1..40205b9 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Face icon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hints"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App opened without being installed."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App opened without being installed. Tap to learn more."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 31509dc..d4adfdc 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el nuevo diseño de la tarea."</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícono de la aplicación"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área de mensajes de ayuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Autenticando tu rostro…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ícono de rostro"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Almacenamiento"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Sugerencias"</string>
<string name="instant_apps" msgid="6647570248119804907">"Apps instantáneas"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Las Apps instantáneas no requieren instalación."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"La app se abrió sin instalarse."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"La app se abrió sin instalarse. Presiona para obtener más información."</string>
<string name="app_info" msgid="6856026610594615344">"Información de apps"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ir al navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Ir a la Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datos móviles"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desactivado"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 2cc3abd..00c179c 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -19,7 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="7164937344850004466">"IU del sistema"</string>
+ <string name="app_label" msgid="7164937344850004466">"UI del sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Quitar de la lista"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Información de la aplicación"</string>
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar diseño de tarea nueva"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensaje de ayuda"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icono de aplicación"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área de mensaje de ayuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscando tu cara…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icono de cara"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -561,13 +563,13 @@
<string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
<string name="output_service_wifi" msgid="3749735218931825054">"Wi‑Fi"</string>
<string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth y Wi‑Fi"</string>
- <string name="system_ui_tuner" msgid="708224127392452018">"Configurador de IU del sistema"</string>
+ <string name="system_ui_tuner" msgid="708224127392452018">"Configurador de UI del sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaje de batería insertado"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar el porcentaje del nivel de batería en el icono de la barra de estado cuando no se esté cargando"</string>
<string name="quick_settings" msgid="10042998191725428">"Ajustes rápidos"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de estado"</string>
<string name="overview" msgid="4018602013895926956">"Aplicaciones recientes"</string>
- <string name="demo_mode" msgid="2532177350215638026">"Modo de demostración de IU del sistema"</string>
+ <string name="demo_mode" msgid="2532177350215638026">"Modo de demostración de UI del sistema"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Habilitar modo de demostración"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostrar modo de demostración"</string>
<string name="status_bar_ethernet" msgid="5044290963549500128">"Ethernet"</string>
@@ -584,12 +586,12 @@
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Zona Wi-Fi"</string>
<string name="accessibility_managed_profile" msgid="6613641363112584120">"Perfil de trabajo"</string>
<string name="tuner_warning_title" msgid="7094689930793031682">"Diversión solo para algunos"</string>
- <string name="tuner_warning" msgid="8730648121973575701">"El configurador de IU del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
+ <string name="tuner_warning" msgid="8730648121973575701">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
<string name="got_it" msgid="2239653834387972602">"Entendido"</string>
- <string name="tuner_toast" msgid="603429811084428439">"¡Enhorabuena! El configurador de IU del sistema se ha añadido a Ajustes"</string>
+ <string name="tuner_toast" msgid="603429811084428439">"¡Enhorabuena! El configurador de UI del sistema se ha añadido a Ajustes"</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Quitar de Ajustes"</string>
- <string name="remove_from_settings_prompt" msgid="6069085993355887748">"¿Quitar el configurador de IU del sistema de Ajustes y dejar de utilizar sus funciones?"</string>
+ <string name="remove_from_settings_prompt" msgid="6069085993355887748">"¿Quitar el configurador de UI del sistema de Ajustes y dejar de utilizar sus funciones?"</string>
<string name="activity_not_found" msgid="348423244327799974">"La aplicación no está instalada en tu dispositivo"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostrar los segundos del reloj"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar a la duración de la batería."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Almacenamiento"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Sugerencias"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplicaciones Instantáneas"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"No es necesario instalar las Aplicaciones Instantáneas."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> se está ejecutando"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"La aplicación se abre sin necesidad de instalarla."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"La aplicación se abre sin necesidad de instalarla. Toca para obtener más información."</string>
<string name="app_info" msgid="6856026610594615344">"Información de la aplicación"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ir al navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Ir a la Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datos móviles"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> ‑ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desactivado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index f22dc1a..246f1f8 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ava kaamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Uue toimingu paigutuse valimine"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Rakenduse ikoon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Abisõnumi ala"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Otsitakse teid …"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Näoikoon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth on ühendatud."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Salvestusruum"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Vihjed"</string>
<string name="instant_apps" msgid="6647570248119804907">"Installimata avatavad rakendused"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Installimata avatavaid rakendusi pole vaja installida."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Rakendus <xliff:g id="APP">%1$s</xliff:g> töötab"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Rakendus avati installimata."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Rakendus avati installimata. Lisateabe saamiseks puudutage."</string>
<string name="app_info" msgid="6856026610594615344">"Rakenduse teave"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ava brauser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Avage veebis"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiilne andmeside"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WiFi on välja lülitatud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 87dba92..b73946e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ireki kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Hautatu zereginen diseinua"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Aplikazioaren ikonoa"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Laguntza-mezuaren eremua"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Zure bila…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Aurpegiaren ikonoa"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoom-bateragarritasunaren botoia."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Handiagotu pantaila txikia."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetootha konektatuta."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Memoria"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Aholkuak"</string>
<string name="instant_apps" msgid="6647570248119804907">"Zuzeneko aplikazioak"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Zuzeneko aplikazioak ez dira instalatu behar."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> exekutatzen ari da"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Ezer instalatu gabe ireki da aplikazioa."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Ezer instalatu gabe ireki da aplikazioa. Sakatu informazio gehiago lortzeko."</string>
<string name="app_info" msgid="6856026610594615344">"Aplikazioari buruzko informazioa"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Joan arakatzailera"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Joan sarera"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datu-konexioa"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi konexioa desaktibatuta dago"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 5411d3d..3997f1a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -47,7 +47,7 @@
<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>
- <string name="status_bar_settings_mute_label" msgid="554682549917429396">"بیصدا"</string>
+ <string name="status_bar_settings_mute_label" msgid="554682549917429396">"صامت"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"خودکار"</string>
<string name="status_bar_settings_notifications" msgid="397146176280905137">"اعلانها"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"اتصال اینترنتی با بلوتوث تلفن همراه"</string>
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"باز کردن دوربین"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"انتخاب طرحبندی جدید کار"</string>
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"بخش پیام راهنما"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأیید"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"حسگر اثر انگشت را لمس کنید"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"نماد اثر انگشت"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"نماد برنامه"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"بخش پیام راهنما"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"درحال جستجوی شما…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"نماد چهره"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحههای کوچک تا بزرگ."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"بلوتوث متصل است."</string>
@@ -539,14 +541,14 @@
<string name="ring_toggle_title" msgid="3281244519428819576">"تماسها"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"زنگ زدن"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"لرزش"</string>
- <string name="volume_ringer_status_silent" msgid="6896394161022916369">"بیصدا"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"صامت"</string>
<string name="qs_status_phone_vibrate" msgid="204362991135761679">"تلفن در حالت لرزش است"</string>
<string name="qs_status_phone_muted" msgid="5437668875879171548">"تلفن بیصدا است"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. برای باصدا کردن ضربه بزنید."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. برای تنظیم روی لرزش ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
- <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. برای بیصدا کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
+ <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. برای صامت کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. برای تنظیم روی لرزش، ضربه بزنید."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. برای بیصدا کردن ضربه بزنید."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. برای صامت کردن ضربه بزنید."</string>
<string name="volume_ringer_hint_mute" msgid="9199811307292269601">"صامت کردن"</string>
<string name="volume_ringer_hint_unmute" msgid="6602880133293060368">"باصدا کردن"</string>
<string name="volume_ringer_hint_vibrate" msgid="4036802135666515202">"لرزش"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"فضای ذخیرهسازی"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"نکات"</string>
<string name="instant_apps" msgid="6647570248119804907">"برنامههای فوری"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"برنامههای فوری نیاز به نصب ندارند."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> درحال اجرا"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"برنامه بدون نصب شدن باز شد."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"برنامه بدون نصب شدن باز شد. برای اطلاعات بیشتر ضربه بزنید."</string>
<string name="app_info" msgid="6856026610594615344">"اطلاعات برنامه"</string>
- <string name="go_to_web" msgid="2650669128861626071">"رفتن به مرورگر"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"رفتن به وب"</string>
<string name="mobile_data" msgid="7094582042819250762">"داده تلفن همراه"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi خاموش است"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 83345b8..6e8cb4c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"avaa kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Valitse uusi tehtävien asettelu"</string>
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Ohjeviestialue"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Vahvista"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Sovelluskuvake"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Ohjeviestialue"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Etsitään kasvoja…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Kasvokuvake"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth yhdistetty."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Tallennustila"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Vihjeet"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Pikasovelluksia ei tarvitse asentaa."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> on käynnissä"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Sovellus avattiin ilman asennusta."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Sovellus avattiin ilman asennusta. Katso lisätietoja napauttamalla."</string>
<string name="app_info" msgid="6856026610594615344">"Sovelluksen tiedot"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Siirry selaimeen"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Avaa verkossa"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiilitiedonsiirto"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi on pois käytöstä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c9c252f..f6d66b7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"Ouvrir l\'appareil photo"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau format de tâche"</string>
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zone de message d\'aide"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icône de l\'application"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Zone de message d\'aide"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icône de visage"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connecté"</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Stockage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Conseils"</string>
<string name="instant_apps" msgid="6647570248119804907">"Applications instantanées"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Les applications instantanées ne nécessitent pas d\'installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Application ouverte sans avoir été installée."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Application ouverte sans avoir été installée. Touchez ici pour en savoir plus."</string>
<string name="app_info" msgid="6856026610594615344">"Détails de l\'application"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ouvrir le navigateur"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Accéder au Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Données cellulaires"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Le Wi-Fi est désactivé"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 4559854..326b7c7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ouvrir l\'appareil photo"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau plan de tâche"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icône d\'application"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Zone de message d\'aide"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icône représentant un visage"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connecté"</string>
@@ -401,9 +403,9 @@
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Aucune\ninterruption"</string>
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priorité\nuniquement"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string>
- <string name="keyguard_indication_charging_time" msgid="2056340799276374421">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rechargement… (rechargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
- <string name="keyguard_indication_charging_time_fast" msgid="7767562163577492332">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rechargement rapide… (à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rechargement lent… (à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time" msgid="2056340799276374421">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge... (à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="7767562163577492332">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge rapide… (à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente… (à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Espace de stockage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Astuces"</string>
<string name="instant_apps" msgid="6647570248119804907">"Applis instantanées"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Les applis instantanées ne nécessitent pas d\'installation."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Vous pouvez ouvrir cette application sans l\'installer."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Vous pouvez ouvrir cette application sans l\'installer. Appuyez pour en savoir plus."</string>
<string name="app_info" msgid="6856026610594615344">"Infos sur l\'appli"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Accéder au navigateur"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Accéder au site Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Données mobiles"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi désactivé"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9cb251e..192c132 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar novo deseño de tarefas"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icona de aplicación"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área de mensaxes de axuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscándote…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icona de cara"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidade"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilidade co tamaño da pantalla."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Almacenamento"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Consellos"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplicacións instantáneas"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"As aplicacións instantáneas non precisan instalación."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Estase executando <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Abriuse a aplicación sen ter que instalala."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Abriuse a aplicación sen ter que instalala. Tocar para obter máis información."</string>
<string name="app_info" msgid="6856026610594615344">"Info. da aplicación"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ir ao navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Acceder á web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datos móbiles"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g>-<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"A wifi está desactivada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index df06cbe..f392268 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"કૅમેરો ખોલો"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"નવું કાર્ય લેઆઉટ પસંદ કરો"</string>
<string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"સહાય સંદેશનું ક્ષેત્ર"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"કન્ફર્મ કરો"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ફિંગરપ્રિન્ટના સેન્સરને સ્પર્શ કરો"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ફિંગરપ્રિન્ટનું આઇકન"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ઍપ્લિકેશનનું આઇકન"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"સહાય સંદેશનું ક્ષેત્ર"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"તમારા માટે શોધી રહ્યાં છે..."</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ચહેરા આઇકન"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"સુસંગતતા ઝૂમ બટન."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"નાનીથી મોટી સ્ક્રીન પર ઝૂમ કરો."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"બ્લૂટૂથ કનેક્ટ થયું."</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"સ્ટોરેજ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"હિન્ટ"</string>
<string name="instant_apps" msgid="6647570248119804907">"ઝટપટ ઍપ્લિકેશનો"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ઝટપટ ઍપ્લિકેશનો માટે ઇન્સ્ટૉલેશનની જરૂર નથી."</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"ઍપ્લિકેશન માહિતી"</string>
- <string name="go_to_web" msgid="2650669128861626071">"બ્રાઉઝર પર જાઓ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"વેબ પર જાઓ"</string>
<string name="mobile_data" msgid="7094582042819250762">"મોબાઇલ ડેટા"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"વાઇ-ફાઇ બંધ છે"</string>
diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/packages/SystemUI/res/values-h320dp/config.xml
deleted file mode 100644
index a9c19db..0000000
--- a/packages/SystemUI/res/values-h320dp/config.xml
+++ /dev/null
@@ -1,22 +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.
-*/
--->
-
-<resources>
- <!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">2</integer>
-</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 46555a4..8795264 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"कैमरा खोलें"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नया कार्य लेआउट चुनें"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"सहायता का मैसेज दिखाने की जगह"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि करें"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फ़िंगरप्रिंट सेंसर को छुएं"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फ़िंगरप्रिंट आइकॉन"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ऐप्लिकेशन आइकॉन"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"सहायता का मैसेज दिखाने की जगह"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"आपको पहचान रहा है…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"चेहरे का आइकॉन"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"संगतता ज़ूम बटन."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"छोटी से बड़ी स्क्रीन पर ज़ूम करें."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लूटूथ कनेक्ट किया गया."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"जगह"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"संकेत"</string>
<string name="instant_apps" msgid="6647570248119804907">"इंस्टेंट ऐप"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"झटपट ऐप्स के लिए इंस्टॉलेशन ज़रूरी नहीं है."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> चल रहा है"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"ऐप्लिकेशन इंस्टॉल किए बिना ही खुल गया है."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"ऐप्लिकेशन इंस्टॉल किए बिना ही खुल गया है. ज़्यादा जानने के लिए टैप करें."</string>
<string name="app_info" msgid="6856026610594615344">"ऐप की जानकारी"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ब्राउज़र पर जाएं"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"वेब पर जाएं"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"वाई-फ़ाई बंद है"</string>
diff --git a/packages/SystemUI/res/values-hi/strings_car.xml b/packages/SystemUI/res/values-hi/strings_car.xml
index 8820046..3beada5 100644
--- a/packages/SystemUI/res/values-hi/strings_car.xml
+++ b/packages/SystemUI/res/values-hi/strings_car.xml
@@ -20,8 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="car_guest" msgid="3738772168718508650">"मेहमान"</string>
- <!-- no translation found for start_guest_session (7055742120180595689) -->
- <skip />
+ <string name="start_guest_session" msgid="7055742120180595689">"मेहमान मोड"</string>
<string name="car_add_user" msgid="5245196248349230898">"उपयोगकर्ता जोड़ें"</string>
<string name="car_new_user" msgid="8142927244990323906">"नया उपयोगकर्ता"</string>
<string name="user_add_user_message_setup" msgid="1791011504259527329">"जब आप कोई नया उपयोगकर्ता जोड़ते हैं तो, उस व्यक्ति को अपनी जगह सेट करनी होती है."</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7ad2fe2..b6fa4f5 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -104,10 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"otvaranje fotoaparata"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi izgled zadataka"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikacije"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Područje poruke za pomoć"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona lica"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Gumb za kompatibilnost zumiranja."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zumiranje manjeg zaslona na veći."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth povezan."</string>
@@ -829,9 +831,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Pohrana"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Savjeti"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant aplikacije"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant aplikacije nije potrebno instalirati."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Izvodi se aplikacija <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikacija je otvorena bez instaliranja."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikacija je otvorena bez instaliranja. Dodirnite da biste saznali više."</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Otvori preglednik"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Prijeđi na web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je isključen"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index f5fad27..9076379 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"kamera megnyitása"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Új feladatelrendezés kiválasztása"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Alkalmazás ikonja"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Súgószöveg területe"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Keresem az Ön arcát…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Arcikon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kompatibilitási zoom gomb."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kicsinyítsen a nagyobb képernyőhöz."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth csatlakoztatva."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Tárhely"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Tippek"</string>
<string name="instant_apps" msgid="6647570248119804907">"Azonnali alkalmazások"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Az azonnali alkalmazásokat nem kell telepíteni."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"A(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazás jelenleg fut"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Az alkalmazás telepítés nélkül lett megnyitva."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Az alkalmazás telepítés nélkül lett megnyitva. Ha további információra van szüksége, koppintson ide."</string>
<string name="app_info" msgid="6856026610594615344">"Alkalmazásinformáció"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ugrás a böngészőbe"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Tovább az internetre"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiladatok"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"A Wi-Fi ki van kapcsolva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 5eeca4c..54401f5 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"բացել ֆոտոխցիկը"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Ընտրել առաջադրանքի նոր դասավորություն"</string>
<string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Օգնության հաղորդագրության դաշտ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Հաստատել"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Հպեք մատնահետքերի սկաներին"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Մատնահետքի պատկերակ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Հավելվածի պատկերակ"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Օգնության հաղորդագրության դաշտ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Դեմքի ճանաչում…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Դեմքի պատկերակ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-ը միացված է:"</string>
@@ -582,12 +584,12 @@
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Թեժ կետ"</string>
<string name="accessibility_managed_profile" msgid="6613641363112584120">"Աշխատանքային պրոֆիլ"</string>
<string name="tuner_warning_title" msgid="7094689930793031682">"Զվարճանք մեկ՝ որոշակի մարդու համար"</string>
- <string name="tuner_warning" msgid="8730648121973575701">"Համակարգի ՕՄ-ի ընդունիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտատիրոջ միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
+ <string name="tuner_warning" msgid="8730648121973575701">"Համակարգի ՕՄ-ի կարգավորիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտատիրոջ միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
<string name="got_it" msgid="2239653834387972602">"Եղավ"</string>
- <string name="tuner_toast" msgid="603429811084428439">"Համակարգի ՕՄ-ի ընդունիչը ավելացվել է կարգավորումներին"</string>
+ <string name="tuner_toast" msgid="603429811084428439">"Համակարգի ՕՄ-ի կարգավորիչը ավելացվել է կարգավորումներին"</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Հեռացնել կարգավորումներից"</string>
- <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Հեռացնե՞լ Համակարգի ՕՄ-ի ընդունիչը կարգավորումներից և չօգտվել այլևս նրա գործառույթներից:"</string>
+ <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Հեռացնե՞լ Համակարգի ՕՄ-ի կարգավորիչը կարգավորումներից և չօգտվել այլևս նրա գործառույթներից:"</string>
<string name="activity_not_found" msgid="348423244327799974">"Հավելվածը տեղադրված չէ սարքի վրա"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Ցույց տալ ժամացույցի վայրկյանները"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ցույց տալ ժամացույցի վայրկյանները կարգավիճակի տողում: Կարող է ազդել մարտկոցի աշխատանքի ժամանակի վրա:"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Տարածք"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Հուշումներ"</string>
<string name="instant_apps" msgid="6647570248119804907">"Ակնթարթային հավելվածներ"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Ակնթարթային հավելվածները տեղադրում չեն պահանջում։"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> հավելվածն աշխատում է"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Հավելվածը բացվել է առանց տեղադրման։"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Հավելվածը բացվել է առանց տեղադրման։ Հպեք՝ ավելին իմանալու համար։"</string>
<string name="app_info" msgid="6856026610594615344">"Հավելվածի տվյալներ"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Անցնել դիտարկիչ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Բացեք համացանցում"</string>
<string name="mobile_data" msgid="7094582042819250762">"Բջջային ինտերնետ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi-ն անջատված է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5a61a64..2474158 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Pilih tata letak tugas baru"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikon aplikasi"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Area pesan bantuan"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari wajah Anda…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikon wajah"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tombol perbesar/perkecil kompatibilitas."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Perbesar dari layar kecil ke besar."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth tersambung."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Penyimpanan"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Petunjuk"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplikasi Instan"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Aplikasi instan tidak perlu diinstal."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> berjalan"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikasi dapat dibuka tanpa perlu diinstal."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikasi dapat dibuka tanpa perlu diinstal. Tap untuk mempelajari lebih lanjut."</string>
<string name="app_info" msgid="6856026610594615344">"Info aplikasi"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Buka browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Buka di web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Data seluler"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi nonaktif"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index fb83ff07..c2a020b 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"opna myndavél"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Velja nýtt útlit verkefna"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Forritstákn"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Svæði hjálparskilaboða"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Leitar að þér ..."</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Andlitstákn"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Hnappur fyrir samhæfisaðdrátt."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aðlaga forrit fyrir lítinn skjá að stærri skjá."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth tengt."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Geymslurými"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Vísbendingar"</string>
<string name="instant_apps" msgid="6647570248119804907">"Skyndiforrit"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Skyndiforrit þurfa ekki uppsetningu."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> er í gangi"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Forrit opnað án þess að vera uppsett."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Forrit opnað án þess að vera uppsett. Ýttu til að fá frekari upplýsingar."</string>
<string name="app_info" msgid="6856026610594615344">"Forritsupplýsingar"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Opna vafra"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Fara á vefinn"</string>
<string name="mobile_data" msgid="7094582042819250762">"Farsímagögn"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Slökkt á Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index c081eaa..aefaa6d 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"apri fotocamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Seleziona un nuovo layout per le attività"</string>
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Area dei messaggi di assistenza"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confermo"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icona dell\'applicazione"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Area dei messaggi di assistenza"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"In attesa del volto…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icona volto"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Pulsante zoom compatibilità."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom inferiore per schermo più grande."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth collegato."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Spazio di archiviazione"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Suggerimenti"</string>
<string name="instant_apps" msgid="6647570248119804907">"App istantanee"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Le app istantanee non richiedono l\'installazione."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"App <xliff:g id="APP">%1$s</xliff:g> in esecuzione"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App aperta senza essere stata installata."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App aperta senza essere stata installata. Tocca per avere ulteriori informazioni."</string>
<string name="app_info" msgid="6856026610594615344">"Informazioni app"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Vai al browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Vai sul Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dati mobili"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi disattivato"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d3d18ae..b83f5df 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"פתח את המצלמה"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"בחר פריסה חדשה להצגת משימות"</string>
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"אזור הודעת עזרה"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"אישור"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"יש לגעת בחיישן טביעות האצבע"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"סמל טביעת אצבע"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"סמל אפליקציה"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"אזור הודעת עזרה"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"מחפש אותך…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"סמל הפנים"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth מחובר."</string>
@@ -194,7 +196,7 @@
<string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"פתח מידע על האפליקציה <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"מפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"הודעה נדחתה."</string>
- <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"לוח הודעות."</string>
+ <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"לוח התראות."</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"הגדרות מהירות."</string>
<string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"מסך נעילה."</string>
<string name="accessibility_desc_settings" msgid="3417884241751434521">"הגדרות"</string>
@@ -266,7 +268,7 @@
<item quantity="other">יש בפנים עוד <xliff:g id="NUMBER_1">%s</xliff:g> הודעות.</item>
<item quantity="one">יש בפנים עוד הודעה <xliff:g id="NUMBER_0">%s</xliff:g>.</item>
</plurals>
- <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"הגדרות עבור הודעות"</string>
+ <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"הגדרת התראות"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"הגדרות <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"המסך יסתובב באופן אוטומטי."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"המסך נעול כעת לרוחב."</string>
@@ -835,9 +837,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"אחסון"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"טיפים"</string>
<string name="instant_apps" msgid="6647570248119804907">"אפליקציות אינסטנט"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"אפליקציות אינסטנט לא דורשות התקנה."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"האפליקציה נפתחת בלי התקנה."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"האפליקציה נפתחת בלי התקנה. אפשר להקיש כדי לקבל מידע נוסף."</string>
<string name="app_info" msgid="6856026610594615344">"פרטי אפליקציה"</string>
- <string name="go_to_web" msgid="2650669128861626071">"מעבר אל הדפדפן"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"התחבר לאינטרנט"</string>
<string name="mobile_data" msgid="7094582042819250762">"נתונים סלולריים"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi כבוי"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 8377f07..e5b0a2b 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"カメラを起動"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"新しいタスクレイアウトの選択"</string>
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ヘルプ メッセージ領域"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"指紋認証センサーをタップしてください"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋アイコン"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"アプリのアイコン"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ヘルプ メッセージ領域"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"顔を認証しています…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"顔アイコン"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetoothに接続済み。"</string>
@@ -346,7 +348,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"警告: 上限は<xliff:g id="DATA_LIMIT">%s</xliff:g>です"</string>
<string name="quick_settings_work_mode_label" msgid="7608026833638817218">"仕事用プロファイル"</string>
- <string name="quick_settings_night_display_label" msgid="3577098011487644395">"読書灯"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜間モード"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"日の入りに ON"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"日の出まで"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"<xliff:g id="TIME">%s</xliff:g> に ON"</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ストレージ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ヒント"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant Apps はインストールせずに利用できます。"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> を実行中"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"アプリをインストールせずに開きました。"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"アプリをインストールせずに開きました。詳細を見るにはタップしてください。"</string>
<string name="app_info" msgid="6856026610594615344">"アプリ情報"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ブラウザに移動"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ウェブページを開く"</string>
<string name="mobile_data" msgid="7094582042819250762">"モバイルデータ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi は OFF です"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index fb880c7..19e9f47 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"კამერის გახსნა"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ახალი ამოცანის განლაგების არჩევა"</string>
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"დამხმარე შეტყობინების არე"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"დადასტურება"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"შეეხეთ თითის ანაბეჭდის სენსორს"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"თითის ანაბეჭდის ხატულა"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"აპლიკაციის ხატულა"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"დახმარების შეტყობინების არეალი"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"მიმდინარეობს თქვენი ძიება…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"სახის ხატულა"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth დაკავშირებულია."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"მეხსიერება"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"მინიშნებები"</string>
<string name="instant_apps" msgid="6647570248119804907">"მყისიერი აპები"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"მყისიერი აპები ინსტალაციას არ საჭიროებს."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"აპი გაიხსნა ინსტალაციის გარეშე."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"აპი გაიხსნა ინსტალაციის გარეშე. შეეხეთ მეტის გასაგებად."</string>
<string name="app_info" msgid="6856026610594615344">"აპის შესახებ"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ბრაუზერზე გადასვლა"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ვებზე გადასვლა"</string>
<string name="mobile_data" msgid="7094582042819250762">"მობილური ინტერნეტი"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi გამორთულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 6e372d7..15f8cd5 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"камераны ашу"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Жаңа тапсырма пішімін таңдау"</string>
<string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Анықтама хабары аумағы"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Растау"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Саусақ ізін оқу сканерін түртіңіз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Саусақ ізі белгішесі"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Қолданба белгішесі"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Анықтама хабары аумағы"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Бет ізделуде…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Бет белгішесі"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Үйлесімділік ұлғайту түймесі."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Үлкендеу экранда кішірейту."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth қосылған."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Жад"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Кеңестер"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Лездік қолданбаларды орнатудың қажеті жоқ."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> іске қосулы"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Қолданба орнатылмай-ақ ашылды."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Қолданба орнатылмай-ақ ашылды. Толығырақ мәлімет алу үшін түртіңіз."</string>
<string name="app_info" msgid="6856026610594615344">"Қолданба ақпараты"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Браузерге өту"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Вебке өту"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобильдік деректер"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi өшірулі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index eb9ed61..af8d4ce 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"បើកម៉ាស៊ីនថត"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ជ្រើសប្លង់ភារកិច្ចថ្មី"</string>
<string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"តំបន់សារជំនួយ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"បញ្ជាក់"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ប៉ះឧបករណ៍ចាប់ស្នាមម្រាមដៃ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"រូបតំណាងស្នាមម្រាមដៃ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"រូបតំណាងកម្មវិធី"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"តំបន់សារជំនួយ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"កំពុងស្វែងរកអ្នក…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"រូបផ្ទៃមុខ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុងពង្រីកត្រូវគ្នា។"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួមអេក្រង់ពីទៅធំ"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"បានតភ្ជាប់ប៊្លូធូស។"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ទំហំផ្ទុក"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ការសម្រួល"</string>
<string name="instant_apps" msgid="6647570248119804907">"កម្មវិធីប្រើភ្លាមៗ"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"កម្មវិធីប្រើភ្លាមៗមិនតម្រូវឲ្យមានការដំឡើងទេ។"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> កំពុងដំណើរការ"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"កម្មវិធីត្រូវបានបើកដោយមិនចាំបាច់ដំឡើង។"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"កម្មវិធីត្រូវបានបើកដោយមិនចាំបាច់ដំឡើង។ ចុចដើម្បីស្វែងយល់បន្ថែម។"</string>
<string name="app_info" msgid="6856026610594615344">"ព័ត៌មានកម្មវិធី"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ចូលទៅកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ចូលទៅកាន់បណ្តាញ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ទិន្នន័យទូរសព្ទចល័ត"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi បានបិទ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index c63dbe9..ac36e73 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ಸಹಾಯ ಸಂದೇಶ ಪ್ರದೇಶ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ದೃಢೀಕರಿಸಿ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ಅಪ್ಲಿಕೇಶನ್ ಐಕಾನ್"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ಸಹಾಯ ಸಂದೇಶ ಪ್ರದೇಶ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ನಿಮಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ಮುಖದ ಐಕಾನ್"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ಹೊಂದಾಣಿಕೆಯ ಝೂಮ್ ಬಟನ್."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ಚಿಕ್ಕ ಪರದೆಯಿಂದ ದೊಡ್ಡ ಪರದೆಗೆ ಝೂಮ್ ಮಾಡು."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ಬ್ಲೂಟೂತ್ ಸಂಪರ್ಕಗೊಂಡಿದೆ."</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ಸಂಗ್ರಹಣೆ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ಸುಳಿವುಗಳು"</string>
<string name="instant_apps" msgid="6647570248119804907">"ತತ್ಕ್ಷಣ ಆಪ್ಗಳು"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ತತ್ಕ್ಷಣ ಆಪ್ಗಳಿಗೆ ಸ್ಥಾಪನೆಯ ಅಗತ್ಯವಿಲ್ಲ."</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ಬ್ರೌಸರ್ಗೆ ಹೋಗಿ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ವೆಬ್ಗೆ ಹೋಗಿ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ವೈ-ಫೈ ಆಫ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 77c983e..1e2ab42 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"카메라 열기"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"새 작업 레이아웃 선택"</string>
<string name="cancel" msgid="6442560571259935130">"취소"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"도움말 메시지 영역"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"확인"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"지문 센서를 터치하세요."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"지문 아이콘"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"애플리케이션 아이콘"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"도움말 메시지 영역"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"찾는 중..."</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"얼굴 아이콘"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"블루투스가 연결되었습니다."</string>
@@ -410,13 +412,13 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"프로필 표시"</string>
<string name="user_add_user" msgid="5110251524486079492">"사용자 추가"</string>
<string name="user_new_user_name" msgid="426540612051178753">"새 사용자"</string>
- <string name="guest_nickname" msgid="8059989128963789678">"손님"</string>
+ <string name="guest_nickname" msgid="8059989128963789678">"게스트"</string>
<string name="guest_new_guest" msgid="600537543078847803">"게스트 추가"</string>
- <string name="guest_exit_guest" msgid="7187359342030096885">"손님 삭제"</string>
- <string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"손님을 삭제하시겠습니까?"</string>
+ <string name="guest_exit_guest" msgid="7187359342030096885">"게스트 삭제"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"게스트를 삭제하시겠습니까?"</string>
<string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"삭제"</string>
- <string name="guest_wipe_session_title" msgid="6419439912885956132">"손님 세션 다시 시작"</string>
+ <string name="guest_wipe_session_title" msgid="6419439912885956132">"게스트 세션 다시 시작"</string>
<string name="guest_wipe_session_message" msgid="8476238178270112811">"세션을 계속 진행하시겠습니까?"</string>
<string name="guest_wipe_session_wipe" msgid="5065558566939858884">"다시 시작"</string>
<string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"예, 계속합니다."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"저장공간"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"힌트"</string>
<string name="instant_apps" msgid="6647570248119804907">"인스턴트 앱"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"인스턴트 앱은 설치가 필요하지 않습니다."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> 실행 중"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"설치 없이 앱이 실행되었습니다."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"설치 없이 앱이 실행되었습니다. 탭하여 자세히 알아보세요."</string>
<string name="app_info" msgid="6856026610594615344">"앱 정보"</string>
- <string name="go_to_web" msgid="2650669128861626071">"브라우저로 이동"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"웹으로 이동"</string>
<string name="mobile_data" msgid="7094582042819250762">"모바일 데이터"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g>, <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi가 사용 중지됨"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index e334da4..585e290 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"камераны ачуу"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Жаңы тапшырманын планын тандаңыз"</string>
<string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Жардам билдирүүсү"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Ырастоо"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Манжа изинин сенсорун басыңыз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Манжа изинин сүрөтчөсү"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Колдонмонун сүрөтчөсү"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Жардам билдирүүсү"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Жүзүңүз изделүүдө…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Жүздүн сүрөтчөсү"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Масштабды сыйыштыруу баскычы."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Кичинекейди чоң экранга масштабдоо."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth байланышта"</string>
@@ -209,7 +211,7 @@
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Учак режими өчүрүлдү."</string>
<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_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>
@@ -276,7 +278,7 @@
<string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"Кошумча параметрлерди ачуу үчүн сүрөтчөлөрдү басып, кармап туруңуз"</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_alarms_label" msgid="2559229444312445858">"Ойготкуч гана"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Тымтырс"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> түзмөк)"</string>
@@ -395,7 +397,7 @@
<string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Толук жымжырттык талап кылынат. Бул экрандагыны окугучтарды да тынчтандырат."</string>
<string name="interruption_level_none" msgid="6000083681244492992">"Тымтырс"</string>
<string name="interruption_level_priority" msgid="6426766465363855505">"Шашылыш эскертмелер гана"</string>
- <string name="interruption_level_alarms" msgid="5226306993448328896">"Ойготкучтар гана"</string>
+ <string name="interruption_level_alarms" msgid="5226306993448328896">"Ойготкуч гана"</string>
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Тым-\nтырс"</string>
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Артыкчылыктуу\nгана"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ойготкучтар\nгана"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Сактагыч"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Кеңештер"</string>
<string name="instant_apps" msgid="6647570248119804907">"Ыкчам ачылуучу колдонмолор"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Ыкчам ачылуучу колдонмолорду орнотуу талап кылынбайт."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Колдонмо орнотулбастан ачылды."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Колдонмо орнотулбастан ачылды. Толугураак маалымат алуу үчүн таптап коюңуз."</string>
<string name="app_info" msgid="6856026610594615344">"Колдонмо тууралуу"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Серепчиге өтүү"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Интернетке өтүү"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилдик Интернет"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi өчүк"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 56f9e1d..0952df4 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ເປີດກ້ອງ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ເລືອກແຜນຜັງໜ້າວຽກໃໝ່"</string>
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ຊ່ວຍພື້ນທີ່ຂໍ້ຄວາມ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ຢືນຢັນ"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ໄອຄອນແອັບພລິເຄຊັນ"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ຊ່ວຍພື້ນທີ່ຂໍ້ຄວາມ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ກຳລັງຊອກຫາທ່ານ…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ໄອຄອນໃບໜ້າ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ປຸ່ມຊູມທີ່ໃຊ້ຮ່ວມກັນໄດ້."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ຊູມຈໍນ້ອຍໄປເປັນຈໍຂະຫນາດໃຫຍ່."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ເຊື່ອມຕໍ່ Bluetooth ແລ້ວ."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ບ່ອນເກັບຂໍ້ມູນ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ຄຳໃບ້"</string>
<string name="instant_apps" msgid="6647570248119804907">"ອິນສະແຕນແອັບ"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ອິນສະແຕນແອັບບໍ່ຈຳເປັນຕ້ອງມີການຕິດຕັ້ງ."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງເຮັດວຽກຢູ່"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"ເປີດແອັບໂດຍບໍ່ມີການຕິດຕັ້ງແລ້ວ."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"ເປີດແອັບໂດຍບໍ່ມີການຕິດຕັ້ງແລ້ວ. ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ."</string>
<string name="app_info" msgid="6856026610594615344">"ຂໍ້ມູນແອັບ"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ໄປທີ່ໂປຣແກຣມທ່ອງເວັບ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ໄປທີ່ເວັບ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ອິນເຕີເນັດມືຖື"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ປິດຢູ່"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index b8ae38b..ac6cfc7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"atidaryti fotoaparatą"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Pasirinkti naują užduoties išdėstymą"</string>
<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>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Palieskite kontrolinio kodo jutiklį"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Kontrolinio kodo piktograma"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Programos piktograma"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Pagalbos pranešimo sritis"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ieškoma jūsų…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Veido piktograma"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Suderinamumo priartinimo mygtukas."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Padidinti ekraną."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"„Bluetooth“ prijungtas."</string>
@@ -835,9 +837,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Saugykla"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Užuominos"</string>
<string name="instant_apps" msgid="6647570248119804907">"Akimirksniu įkeliamos programos"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Akimirksniu įkeliamų programų nereikia įdiegti."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Programa „<xliff:g id="APP">%1$s</xliff:g>“ paleista"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Programa atidaryta jos neįdiegus."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Programa atidaryta jos neįdiegus. Palieskite, kad sužinotumėte daugiau."</string>
<string name="app_info" msgid="6856026610594615344">"Programos informacija"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Eiti į naršyklę"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Eiti į žiniatinklį"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilieji duomenys"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g>–<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"„Wi-Fi“ išjungtas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4a0ee10..01e2e39 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -104,10 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"atvērt kameru"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Atlasiet jaunu uzdevumu izkārtojumu"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Lietojumprogrammas ikona"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Palīdzības ziņojuma apgabals"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Notiek jūsu sejas meklēšana…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Sejas ikona"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Saderības tālummaiņas poga."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Veikt tālummaiņu no mazāka ekrāna uz lielāku."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth savienojums ir izveidots."</string>
@@ -829,9 +831,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Krātuve"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Padomi"</string>
<string name="instant_apps" msgid="6647570248119804907">"Tūlītējās lietotnes"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Tūlītējām lietotnēm nav nepieciešama instalēšana."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Lietotne <xliff:g id="APP">%1$s</xliff:g> darbojas"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Lai atvērtu šo lietotni, tā nav jāinstalē."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Lai atvērtu šo lietotni, tā nav jāinstalē. Pieskarieties, lai uzzinātu vairāk."</string>
<string name="app_info" msgid="6856026610594615344">"Lietotnes informācija"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Atvērt pārlūku"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Pāriet uz tīmekli"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilie dati"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ir izslēgts"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index bf9cc35..8b4b458 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"отвори камера"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Изберете нов распоред на задача"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област за пораки за помош"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Допрете го сензорот за отпечатоци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатоци"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Икона за апликацијата"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Поле за пораки за помош"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ве бараме вас…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Икона за лице"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Копче за компатибилност на зум."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумот е помал на поголем екран."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth е поврзан."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Капацитет"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Совети"</string>
<string name="instant_apps" msgid="6647570248119804907">"Инстант апликации"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Инстант апликациите нема потреба да се инсталираат."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Се извршува <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Апликацијата беше отворена без да се инсталира."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Апликацијата беше отворена без да се инсталира. Допрете за да дознаете повеќе."</string>
<string name="app_info" msgid="6856026610594615344">"Информации за апликација"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Одете на прелистувач"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Одете на интернет"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилен интернет"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi е исклучено"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fefd073..fc32b88 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ക്യാമറ തുറക്കുക"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"പുതിയ ടാസ്ക് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"സഹായ സന്ദേശ ഏരിയ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"സ്ഥിരീകരിക്കുക"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"വിരലടയാള സെൻസർ സ്പർശിക്കുക"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"വിരലടയാള ഐക്കൺ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ആപ്പ് ഐക്കൺ"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"സഹായ സന്ദേശ ഏരിയ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"നിങ്ങൾക്കായി തിരയുന്നു…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"മുഖത്തിന്റെ ഐക്കൺ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"അനുയോജ്യതാ സൂം ബട്ടൺ."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ചെറുതിൽ നിന്ന് വലിയ സ്ക്രീനിലേക്ക് സൂം ചെയ്യുക."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ബ്ലൂടൂത്ത് കണക്റ്റുചെയ്തു."</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"സ്റ്റോറേജ്"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"സൂചനകൾ"</string>
<string name="instant_apps" msgid="6647570248119804907">"ഇൻസ്റ്റന്റ് ആപ്പ്"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ഇൻസ്റ്റന്റ് ആപ്പിന് ഇൻസ്റ്റലേഷൻ ആവശ്യമില്ല."</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"ആപ്പ് വിവരം"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ബ്രൗസറിലേക്ക് പോവുക"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"വെബിൽ പോവുക"</string>
<string name="mobile_data" msgid="7094582042819250762">"മൊബൈൽ ഡാറ്റ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"വൈഫൈ ഓഫാണ്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index e86dc64..331e9b7 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -101,10 +101,12 @@
<string name="camera_label" msgid="7261107956054836961">"камер нээх"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Шинэ ажиллах талбарыг сонгоно уу"</string>
<string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Тусламжийн зурвасын хэсэг"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Баталгаажуулах"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Хурууны хээ мэдрэгчид хүрэх"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Хурууны хээний дүрс тэмдэг"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Аппын дүрс тэмдэг"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Туслах зурвасын хэсэг"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Таныг хайж байна…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Царайны дүрс тэмдэг"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth холбогдсон."</string>
@@ -821,9 +823,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Хадгалах сан"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Заавар"</string>
<string name="instant_apps" msgid="6647570248119804907">"Шуурхай апп"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Шуурхай аппыг суулгах шаардлагагүй."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g>-г ажиллуулж байна"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Аппыг суулгахгүйгээр нээсэн."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Аппыг суулгахгүйгээр нээсэн. Нэмэлт мэдээлэл авахын тулд товшино уу."</string>
<string name="app_info" msgid="6856026610594615344">"Апп-н мэдээлэл"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Хөтчид очих"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Вэбэд очих"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобайл дата"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi унтраалттай байна"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index a9f9af4..33761ca 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"कॅमेरा उघडा"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नवीन कार्य लेआउट निवडा"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"मदत मेसेज परिसर"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"खात्री करा"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिंट सेन्सरला स्पर्श करा"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिंट आयकन"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"अॅप्लिकेशन आयकन"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"मदत मेसेज क्षेत्र"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तुमच्यासाठी शोधत आहे…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"चेहरा आयकन"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"सुसंगतता झूम बटण."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"लहानपासून मोठ्या स्क्रीनवर झूम करा."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लूटूथ कनेक्ट केले."</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"स्टोरेज"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"सूचना"</string>
<string name="instant_apps" msgid="6647570248119804907">"इन्सटंट अॅप्स"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"इन्सटंट अॅप्सना स्थापनेची आवश्यकता नसते."</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ब्राउझरवर जा"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"वेबवर जा"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"वाय-फाय बंद आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 6ec7889..d6a71ef 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Pilih reka letak tugas baharu"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikon aplikasi"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Bahagian mesej bantuan"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari anda…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikon wajah"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth disambungkan."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storan"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Pembayang"</string>
<string name="instant_apps" msgid="6647570248119804907">"Apl Segera"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Apl segera tidak memerlukan pemasangan."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> sedang berjalan"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Apl dibuka tanpa dipasang."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Apl dibuka tanpa dipasang. Ketik untuk mengetahui lebih lanjut."</string>
<string name="app_info" msgid="6856026610594615344">"Maklumat apl"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Pergi ke penyemak imbas"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Pergi ke web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Data mudah alih"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi dimatikan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 7afb3b9..2a6e3c2 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
<string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"အကူအညီမက်ဆေ့ဂျ် နေရာ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"အတည်ပြုပါ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"လက်ဗွေအာရုံခံကိရိယာကို တို့ပါ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"လက်ဗွေ သင်္ကေတ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"အပလီကေးရှင်း သင်္ကေတ"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"အကူအညီမက်ဆေ့ဂျ် နေရာ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"သင့်ကို ရှာဖွေနေသည်…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"မျက်နှာသင်္ကေတ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"အံဝင်ခွင်ကျ ဇူးမ်ခလုတ်"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ဖန်သားပြင်ပေါ်တွင် အသေးမှအကြီးသို့ ဇူးမ်ဆွဲခြင်း"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ဘလူးတုသ်ချိတ်ဆက်ထားမှု"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"သိုလှောင်မှုများ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"အရိပ်အမြွက်များ"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ချက်ခြင်းသုံးအက်ပ်များကို ထည့်သွင်းစရာမလိုပါ။"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> လုပ်ဆောင်နေသည်"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"အက်ပ်ကိုမထည့်သွင်းဘဲ ဖွင့်ထားသည်။"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"အက်ပ်ကိုမထည့်သွင်းဘဲ ဖွင့်ထားသည်။ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
<string name="app_info" msgid="6856026610594615344">"အက်ပ်အချက်အလက်"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ဘရောင်ဇာသို့ သွားပါ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ဝဘ်သို့ သွားရန်"</string>
<string name="mobile_data" msgid="7094582042819250762">"မိုဘိုင်းဒေတာ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> —<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ကို ပိတ်ထားသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index b817877..d90eba3 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"åpne kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Velg en ny utforming for oppgaver"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område for hjelpemelding"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekreft"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Appikon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Område for hjelpemelding"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ser etter deg …"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ansiktikon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoomknapp for kompatibilitet."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom fra mindre til større skjerm."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth er tilkoblet."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Lagring"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hint"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Du trenger ikke å installere instant-apper."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> kjører"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Appen ble åpnet uten at den ble installert."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Appen ble åpnet uten at den ble installert. Trykk for å finne ut mer."</string>
<string name="app_info" msgid="6856026610594615344">"Info om appen"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Gå til nettleser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Gå til nettstedet"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobildata"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi er av"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 6cc405f..4e3cddd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"क्यामेरा खोल्नुहोस्"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नयाँ कार्य लेआउट चयन गर्नुहोस्"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"मद्दतसम्बन्धी सन्देशको क्षेत्र"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि गर्नुहोस्"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिन्ट जनाउने आइकन"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"अनुप्रयोग जनाउने आइकन"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"मद्दतसम्बन्धी सन्देशको क्षेत्र"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तपाईंलाई खोज्दै…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"अनुहारको आइकन"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"मिलाउने जुम बटन।"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"स्क्रिनलाई सानोबाट ठूलो पार्नुहोस्।"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लुटुथ जडान भयो।"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"भण्डारण"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"सङ्केतहरू"</string>
<string name="instant_apps" msgid="6647570248119804907">"तात्कालिक अनुप्रयोगहरू"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"तात्कालिक अनुप्रयोगहरूलाई स्थापना गर्नु पर्दैन|"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> चलिरहेको छ"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"स्थापना नगरिकनै अनुप्रयोग खोलियो।"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"स्थापना नगरिकनै अनुप्रयोग खोलियो। थप जान्न ट्याप गर्नुहोस्।"</string>
<string name="app_info" msgid="6856026610594615344">"अनुप्रयोगका बारे जानकारी"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ब्राउजरमा जानुहोस्"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"वेबमा जानुहोस्"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi‑Fi निष्क्रिय छ"</string>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
new file mode 100644
index 0000000..3ab6b56
--- /dev/null
+++ b/packages/SystemUI/res/values-night/styles.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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" />
+
+ <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
+
+ <style name="Theme.SystemUI.Dialog.GlobalActions" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="android:windowIsFloating">true</item>
+ </style>
+
+</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e8c6f8d..9dda44e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"camera openen"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Nieuwe taakindeling selecteren"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"App-pictogram"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Gebied voor Help-berichten"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Jouw gezicht zoeken…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Gezichtspictogram"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knop voor compatibiliteitszoom."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kleiner scherm uitzoomen naar groter scherm."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-verbinding ingesteld."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Opslag"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Hints"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant-apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant-apps hoeven niet te worden geïnstalleerd."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> actief"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"App geopend zonder dat deze is geïnstalleerd."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"App geopend zonder dat deze is geïnstalleerd. Tik voor meer informatie."</string>
<string name="app_info" msgid="6856026610594615344">"App-info"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ga naar browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Ga naar internet"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiele data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wifi is uitgeschakeld"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 277181f..91c62bb 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"କ୍ୟାମେରା ଖୋଲନ୍ତୁ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ନୂଆ ଟାସ୍କ ଲେଆଉଟ୍ ଚୟନ କରନ୍ତୁ"</string>
<string name="cancel" msgid="6442560571259935130">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ସାହାଯ୍ୟ ମେସେଜ୍ କ୍ଷେତ୍ର"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନସର୍କୁ ଛୁଅଁନ୍ତୁ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ଆପ୍ଲିକେଶନ୍ ଆଇକନ୍"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ହେଲ୍ପ ମେସେଜ୍ କ୍ଷେତ୍ର"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ଆପଣଙ୍କୁ ଚିହ୍ନଟ କରୁଛି…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ମୁହଁ ଆଇକନ୍"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"କମ୍ପାଟିବିଲିଟୀ ଜୁମ୍ ବଟନ୍।"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ଜୁମ୍ କରି ସ୍କ୍ରୀନ୍କୁ ଛୋଟରୁ ବଡ଼ କରନ୍ତୁ।"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ବ୍ଲୁ-ଟୂଥ୍ ସଂଯୋଗ କରାଯାଇଛି।"</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ଷ୍ଟୋରେଜ୍"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ହିଣ୍ଟ"</string>
<string name="instant_apps" msgid="6647570248119804907">"ଇନଷ୍ଟାଣ୍ଟ ଆପ୍"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ଇନଷ୍ଟାଣ୍ଟ ଆପ୍ ଇନଷ୍ଟଲ୍ କରିବାର ଆବଶ୍ୟକତା ନାହିଁ"</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"ଆପ୍ ସୂଚନା"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ବ୍ରାଉଜର୍କୁ ଯାଆନ୍ତୁ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ୱେବକୁ ଯାଆନ୍ତୁ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ମୋବାଇଲ୍ ଡାଟା"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ୱାଇ-ଫାଇ ଅଫ୍ ଅଛି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 6509871..96fb641f5 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"ਕੈਮਰਾ ਖੋਲ੍ਹੋ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ਨਵਾਂ ਕੰਮ ਲੇਆਉਟ ਚੁਣੋ"</string>
<string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ਮਦਦ ਸੁਨੇਹਾ ਖੇਤਰ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ਪੁਸ਼ਟੀ ਕਰੋ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਪ੍ਰਤੀਕ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਪ੍ਰਤੀਕ"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ਮਦਦ ਸੁਨੇਹਾ ਖੇਤਰ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ਤੁਹਾਡੀ ਪਛਾਣ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ਚਿਹਰਾ ਪ੍ਰਤੀਕ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ਅਨੁਰੂਪਤਾ ਜ਼ੂਮ ਬਟਨ।"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ਵੱਡੀ ਸਕ੍ਰੀਨ ਤੇ ਛੋਟਾ ਜ਼ੂਮ ਕਰੋ।"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ਕਨੈਕਟ ਕੀਤੀ।"</string>
@@ -344,7 +346,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਸੀਮਾ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਚਿਤਾਵਨੀ"</string>
<string name="quick_settings_work_mode_label" msgid="7608026833638817218">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
- <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ਰਾਤਰੀ ਲਾਈਟ"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ਰਾਤ ਦੀ ਰੋਸ਼ਨੀ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"ਸੂਰਜ ਛਿਪਣ \'ਤੇ ਚਾਲੂ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਚਾਲੂ"</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ਸਟੋਰੇਜ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ਸੰਕੇਤ"</string>
<string name="instant_apps" msgid="6647570248119804907">"ਤਤਕਾਲ ਐਪਾਂ"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ਤਤਕਾਲ ਐਪਾਂ ਨੂੰ ਸਥਾਪਨਾ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"ਐਪ ਜਾਣਕਾਰੀ"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ਬ੍ਰਾਊਜ਼ਰ \'ਤੇ ਜਾਓ"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ਵੈੱਬ \'ਤੇ ਜਾਓ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ਵਾਈ-ਫਾਈ ਬੰਦ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 2959a8d..28d24bf 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"otwórz aparat"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Wybierz nowy układ zadań"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikacji"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Obszar komunikatu pomocy"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Szukam Cię…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona twarzy"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth połączony."</string>
@@ -835,9 +837,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Pamięć wewnętrzna"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Wskazówki"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplikacje błyskawiczne"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Aplikacji błyskawicznych nie trzeba instalować."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> działa"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikacja została otwarta bez zainstalowania."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikacja została otwarta bez zainstalowania. Kliknij, by dowiedzieć się więcej."</string>
<string name="app_info" msgid="6856026610594615344">"O aplikacji"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Otwórz przeglądarkę"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Wejdź na stronę internetową"</string>
<string name="mobile_data" msgid="7094582042819250762">"Komórkowa transmisja danych"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi jest wyłączone"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index ba50c13..ab0f5f2 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícone do app"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área da mensagem de ajuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ícone facial"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Armazenamento"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Dicas"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Os Instant Apps não requerem instalação."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"O app é aberto sem precisar ser instalado."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"O app é aberto sem precisar ser instalado. Toque para saber mais."</string>
<string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Abrir o navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Acessar a Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"O Wi-Fi está desativado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a03dba2..fb0675a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"abrir câmara"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo esquema de tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícone de aplicação"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área da mensagem de ajuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"À sua procura…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ícone de rosto"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão zoom de compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom menor para ecrã maior."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ligado."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Armazenamento"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Sugestões"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplicações instantâneas"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"As Aplicações instantâneas não requerem instalação."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"A aplicação é aberta sem ser instalada."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"A aplicação é aberta sem ser instalada. Toque para saber mais."</string>
<string name="app_info" msgid="6856026610594615344">"Info. da aplicação"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Ir para o navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Aceder à Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desativado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index ba50c13..ab0f5f2 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícone do app"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área da mensagem de ajuda"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ícone facial"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Armazenamento"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Dicas"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Os Instant Apps não requerem instalação."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"O app é aberto sem precisar ser instalado."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"O app é aberto sem precisar ser instalado. Toque para saber mais."</string>
<string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Abrir o navegador"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Acessar a Web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"O Wi-Fi está desativado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 149803b..9b5f5e1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -104,10 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"deschideți camera foto"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selectați noul aspect pentru activitate"</string>
<string name="cancel" msgid="6442560571259935130">"Anulați"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zona mesajelor de ajutor"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmați"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Pictograma aplicației"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Zona mesajelor de ajutor"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Vă căutăm…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Pictograma chip"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceți zoom de la o imagine mai mică la una mai mare."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Conectat prin Bluetooth."</string>
@@ -831,9 +833,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Stocare"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Indicii"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplicații instantanee"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Aplicațiile instantanee nu necesită instalare."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> rulează"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplicația a fost deschisă fără a fi instalată."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplicația a fost deschisă fără a fi instalată. Atingeți pentru a afla mai multe."</string>
<string name="app_info" msgid="6856026610594615344">"Informații despre aplicație"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Accesați browserul"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Accesați pe web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Date mobile"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Conexiunea Wi-Fi este dezactivată"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 7a756cd..8069317 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"Открыть камеру."</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Выберите другой макет"</string>
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Справочное сообщение"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Подтвердить"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Прикоснитесь к сканеру отпечатков пальцев."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок отпечатка пальца"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Значок приложения"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Справочное сообщение"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Поиск лица…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Значок лица"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-соединение установлено."</string>
@@ -837,9 +839,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Хранилище"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Подсказки"</string>
<string name="instant_apps" msgid="6647570248119804907">"Приложения с мгновенным запуском"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Приложения с мгновенным запуском не требуется устанавливать."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> уже здесь!"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Приложение готово к работе, установка не требуется."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Приложение готово к работе, установка не требуется. Нажмите, чтобы узнать больше."</string>
<string name="app_info" msgid="6856026610594615344">"О приложении"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Перейти в браузер"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Перейти в браузер"</string>
<string name="mobile_data" msgid="7094582042819250762">"Моб. Интернет"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Модуль Wi-Fi отключен"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index e29aeda..8802833 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"කැමරාව විවෘත කරන්න"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"නව කාර්යය සැකැස්ම තෝරන්න"</string>
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"උදවු පණිවිඩ ප්රදේශය"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"තහවුරු කරන්න"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ඇඟිලි සලකුණු නිරූපකය"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"යෙදුම් නිරූපකය"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"උදවු පණිවිඩ ප්රදේශය"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ඔබව සොයමින්…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"මුහුණ නිරූපකය"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ගැළපෙන විශාලන බොත්තම."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"විශාල තිරය වෙත කුඩාව විශාලනය කරන්න."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"බ්ලූටූත් සම්බන්ධිතයි."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"ගබඩාව"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"ඉඟි"</string>
<string name="instant_apps" msgid="6647570248119804907">"ක්ෂණික යෙදුම්"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"ක්ෂණික යෙදුම් ස්ථාපනය කිරීම අවශ්ය නොවේ."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> ධාවනය වෙමින්"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"ස්ථාපනය නොකර යෙදුම විවෘත කර ඇත."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"ස්ථාපනය නොකර යෙදුම විවෘත කර ඇත. තව දැන ගැනීමට තට්ටු කරන්න."</string>
<string name="app_info" msgid="6856026610594615344">"යෙදුම් තොරතුරු"</string>
- <string name="go_to_web" msgid="2650669128861626071">"බ්රවුසරය වෙත යන්න"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"වෙබය වෙත යන්න"</string>
<string name="mobile_data" msgid="7094582042819250762">"ජංගම දත්ත"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ක්රියා විරහිතයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 8193e3f..233a81b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"spustiť fotoaparát"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Vyberte nové rozloženie úlohy"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikácie"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Oblasť chybového hlásenia"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hľadáme vás…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona tváre"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth pripojené."</string>
@@ -837,9 +839,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Úložisko"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Tipy"</string>
<string name="instant_apps" msgid="6647570248119804907">"Okamžité aplikácie"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Okamžité aplikácie nevyžadujú inštaláciu."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Aplikácia <xliff:g id="APP">%1$s</xliff:g> je spustená"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikácia bola otvorená bez inštalácie."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikácia bola otvorená bez inštalácie. Klepnutím zobrazíte ďalšie informácie."</string>
<string name="app_info" msgid="6856026610594615344">"Info o aplikácii"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Otvoriť prehliadač"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Prejsť na internet"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilné dáta"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Pripojenie Wi‑Fi je vypnuté"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 71694417..c7e661a 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"odpri fotoaparat"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Izberite novo postavitev opravil"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikacije"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Območje sporočila pomoči"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Preverjanje vašega obraza …"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona obraza"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Gumb povečave za združljivost."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Povečava manjšega na večji zaslon."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Povezava Bluetooth vzpostavljena."</string>
@@ -837,9 +839,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Shramba"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Namigi"</string>
<string name="instant_apps" msgid="6647570248119804907">"Nenamestljive aplikacije"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Nenamestljivih aplikacij ni treba namestiti."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> se izvaja"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikacija je odprta brez namestitve."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikacija je odprta brez namestitve. Dotaknite se, če želite izvedeti več."</string>
<string name="app_info" msgid="6856026610594615344">"Podatki o aplikaciji"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Odpri brskalnik"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Pojdi v splet"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podatki"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je izklopljen"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f2903ef..f4d3d2a 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"hap kamerën"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Zgjidh strukturën e re të detyrës"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona e aplikacionit"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Zona e mesazhit të ndihmës"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Po të kërkojmë…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ikona e fytyrës"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butoni i zmadhimit të pajtueshmërisë."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zmadho nga një ekran i vogël në të madh."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Pajisja është lidhur me \"bluetooth\"."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Hapësira ruajtëse"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Sugjerimet"</string>
<string name="instant_apps" msgid="6647570248119804907">"Aplikacionet e çastit"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Aplikacionet e çastit nuk kërkojnë instalim."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Po ekzekutohet <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Aplikacioni u hap pa u instaluar."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Aplikacioni u hap pa u instaluar. Trokit për të mësuar më shumë."</string>
<string name="app_info" msgid="6856026610594615344">"Informacioni mbi aplikacionin"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Shko te shfletuesi"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Shko në ueb"</string>
<string name="mobile_data" msgid="7094582042819250762">"Të dhënat celulare"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi është joaktiv"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e77b080..5825231 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -104,10 +104,12 @@
<string name="camera_label" msgid="7261107956054836961">"отвори камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Изабери нови распоред задатака"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област поруке за помоћ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Додирните сензор за отисак прста"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона отиска прста"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Икона апликације"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Област поруке за помоћ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Тражимо вас…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Икона лица"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Дугме Зум компатибилности."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумирање са мањег на већи екран."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth је прикључен."</string>
@@ -829,9 +831,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Меморијски простор"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Савети"</string>
<string name="instant_apps" msgid="6647570248119804907">"Инстант апликације"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Инстант апликације не захтевају инсталацију."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Апликација се отворила без инсталирања."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Апликација се отворила без инсталирања. Додирните да бисте сазнали више."</string>
<string name="app_info" msgid="6856026610594615344">"Информације о апликацији"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Иди на прегледач"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Иди на веб"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилни подаци"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi је искључен"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index ccc498e..7040ec2 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"öppna kameran"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Välj en ny layout för uppgiften"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Appikon"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Område för hjälpmeddelande"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Håller utkik efter dig …"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Ansiktsikon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knapp för kompatibilitetszoom."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zooma mindre skärm till större."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ansluten."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Lagring"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Tips"</string>
<string name="instant_apps" msgid="6647570248119804907">"Snabbappar"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Snabbappar behöver inte installeras."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> körs"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Appen öppnades utan installation."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Appen öppnades utan installation. Tryck om du vill veta mer."</string>
<string name="app_info" msgid="6856026610594615344">"Info om appen"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Öppna webbläsaren"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Öppna webbplatsen"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobildata"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi är inaktiverat"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 83e9997..0633e8a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"fungua kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Chagua muundo mpya wa kazi"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Sehemu ya ujumbe wa usaidizi"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Thibitisha"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Aikoni ya programu"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Sehemu ya ujumbe wa usaidizi"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Inakutafuta…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Aikoni ya uso"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth imeunganishwa."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Hifadhi"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Vidokezo"</string>
<string name="instant_apps" msgid="6647570248119804907">"Programu Zinazofunguka Papo Hapo"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Huhitaji kusakinisha programu zinazofunguka papo hapo."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> inaendelea kutumika"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Programu inafunguka bila kusakinishwa."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Programu inafunguka bila kusakinishwa. Gusa ili upate maelezo zaidi."</string>
<string name="app_info" msgid="6856026610594615344">"Maelezo ya programu"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Tumia kivinjari"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Nenda kwenye wavuti"</string>
<string name="mobile_data" msgid="7094582042819250762">"Data ya simu"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g><xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi imezimwa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6f06ab5..469e797 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"கேமராவைத் திற"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"புதிய பணி தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"உதவிச் செய்திக்கான பகுதி"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"உறுதிப்படுத்துக"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"கைரேகை உணர்வியைத் தொடவும்"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"கைரேகை ஐகான்"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"பயன்பாட்டு ஐகான்"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"உதவிச் செய்திக்கான பகுதி"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"உங்கள் முகத்தைத் தேடுகிறது…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"முக ஐகான்"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"பொருந்துமாறு அளவை மாற்றும் பொத்தான்."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"சிறியதிலிருந்து பெரிய திரைக்கு அளவை மாற்றும்."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"புளூடூத் இணைக்கப்பட்டது."</string>
@@ -344,9 +346,9 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> வரம்பு"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> எச்சரிக்கை"</string>
<string name="quick_settings_work_mode_label" msgid="7608026833638817218">"பணி விவரம்"</string>
- <string name="quick_settings_night_display_label" msgid="3577098011487644395">"இரவு ஒளி"</string>
+ <string name="quick_settings_night_display_label" msgid="3577098011487644395">"நைட் லைட்"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"மாலையில் ஆன் செய்"</string>
- <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"சூரிய உதயம் வரை"</string>
+ <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"காலை வரை"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string>
<string name="quick_settings_secondary_label_until" msgid="2749196569462600150">"<xliff:g id="TIME">%s</xliff:g> வரை"</string>
<string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"சேமிப்பிடம்"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"குறிப்புகள்"</string>
<string name="instant_apps" msgid="6647570248119804907">"இன்ஸ்டண்ட் ஆப்ஸ்"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"இன்ஸ்டண்ட் பயன்பாடுகளுக்கு நிறுவல் தேவையில்லை."</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"பயன்பாட்டுத் தகவல்"</string>
- <string name="go_to_web" msgid="2650669128861626071">"உலாவிக்குச் செல்"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"இணையத்திற்குச் செல்"</string>
<string name="mobile_data" msgid="7094582042819250762">"மொபைல் டேட்டா"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"வைஃபை முடக்கத்தில் உள்ளது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 7c09699..5d869f1 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"కెమెరాను తెరువు"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"కొత్త విధి లేఅవుట్ను ఎంచుకోండి"</string>
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"సహాయ సందేశ ప్రాంతం"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"నిర్ధారించు"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"వేలిముద్ర సెన్సార్ను తాకండి"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"వేలిముద్ర చిహ్నం"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"అప్లికేషన్ చిహ్నం"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"సహాయ సందేశ ప్రాంతం"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"మీ కోసం చూస్తోంది…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ముఖ చిహ్నం"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"అనుకూలత జూమ్ బటన్."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"చిన్న స్క్రీన్ నుండి పెద్దదానికి జూమ్ చేయండి."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"బ్లూటూత్ కనెక్ట్ చేయబడింది."</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"నిల్వ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"సూచనలు"</string>
<string name="instant_apps" msgid="6647570248119804907">"తక్షణ యాప్లు"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"తక్షణ అనువర్తనాలకు ఇన్స్టాలేషన్ అవసరం లేదు."</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"యాప్ సమాచారం"</string>
- <string name="go_to_web" msgid="2650669128861626071">"బ్రౌజర్కు వెళ్లండి"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"వెబ్కు వెళ్లు"</string>
<string name="mobile_data" msgid="7094582042819250762">"మొబైల్ డేటా"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ఆఫ్లో ఉంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1d59868..408c5a0 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"เปิดกล้อง"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"เลือกรูปแบบงานใหม่"</string>
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"พื้นที่ข้อความช่วยเหลือ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ยืนยัน"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"แตะเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ไอคอนลายนิ้วมือ"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ไอคอนแอปพลิเคชัน"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"พื้นที่ข้อความช่วยเหลือ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"กำลังหาใบหน้าคุณ…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ไอคอนใบหน้า"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"เชื่อมต่อบลูทูธแล้ว"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"พื้นที่เก็บข้อมูล"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"คำแนะนำ"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant App"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Instant Apps ไม่ต้องใช้การติดตั้ง"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> ทำงานอยู่"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"เปิดแอปได้โดยไม่ต้องติดตั้ง"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"เปิดแอปได้โดยไม่ต้องติดตั้ง แตะเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="app_info" msgid="6856026610594615344">"ข้อมูลแอป"</string>
- <string name="go_to_web" msgid="2650669128861626071">"ไปที่เบราว์เซอร์"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ไปที่เว็บ"</string>
<string name="mobile_data" msgid="7094582042819250762">"เน็ตมือถือ"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ปิดอยู่"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 013acd4..8da4946 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"buksan ang camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Pumili ng bagong layout ng gawain"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icon ng application"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Lugar ng mensahe ng tulong"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hinahanap ka…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Face icon"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Nakakonekta ang Bluetooth."</string>
@@ -346,7 +348,7 @@
<string name="quick_settings_work_mode_label" msgid="7608026833638817218">"Profile sa trabaho"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Mao-on sa sunset"</string>
- <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Hanggang mag-umaga"</string>
+ <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Hanggang sunrise"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"Mao-on sa ganap na <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="2749196569462600150">"Hanggang <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Mga Hint"</string>
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Hindi kailangang i-install ang mga instant na app."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"Tumatakbo ang <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Nabuksan ang app nang hindi ini-install."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Nabuksan ang app nang hindi ini-install. I-tap para matuto pa."</string>
<string name="app_info" msgid="6856026610594615344">"Impormasyon ng app"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Pumunta sa browser"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Pumunta sa web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Naka-off ang Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 9030113..de8ed9d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"kamerayı aç"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Yeni görev düzenini seçin"</string>
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yardım mesajı alanı"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Onaylayın"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Uygulama simgesi"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Yardım mesajı alanı"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yüzünüz tanınmaya çalışılıyor…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Yüz simgesi"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth bağlandı."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Depolama alanı"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"İpuçları"</string>
<string name="instant_apps" msgid="6647570248119804907">"Hazır Uygulamalar"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Hazır uygulamaların yüklenmesi gerekmez."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Uygulama yüklenmeden açıldı."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Uygulama yüklenmeden açıldı. Daha fazla bilgi için dokunun."</string>
<string name="app_info" msgid="6856026610594615344">"Uygulama bilgileri"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Tarayıcıya git"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Web\'e git"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobil veriler"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Kablosuz bağlantı kapalı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index cf21c0f..6c2e661 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -105,10 +105,12 @@
<string name="camera_label" msgid="7261107956054836961">"відкрити камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Виберіть новий макет завдання"</string>
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Область довідкового повідомлення"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Підтвердити"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Торкніться сканера відбитків пальців"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок відбитка пальця"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Значок додатка"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Область довідкового повідомлення"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Пошук обличчя…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Значок обличчя"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабування сумісності."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth під’єднано."</string>
@@ -837,9 +839,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Пам’ять"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Поради"</string>
<string name="instant_apps" msgid="6647570248119804907">"Додатки з миттєвим запуском"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Додатки з миттєвим запуском не потрібно встановлювати."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> працює"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Додаток відкрито без встановлення."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Додаток відкрито без встановлення. Торкніться, щоб дізнатися більше."</string>
<string name="app_info" msgid="6856026610594615344">"Про додаток"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Веб-переглядач"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Перейти на веб-сайт"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобільний трафік"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi вимкнено"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index d2ee8c7..88391b0 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"کیمرا کھولیں"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"نئے کام کا لے آؤٹ منتخب کریں"</string>
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"امدادی پیغام کا علاقہ"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"تصدیق کریں"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"فنگر پرنٹ سینسر پر ٹچ کریں"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"فنگر پرنٹ آئیکن"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ایپلیکیشن کا آئیکن"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"امدادی پیغام کا علاقہ"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"آپ کے لیے تلاش کیا جا رہا ہے…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"چہرے کا آئیکن"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"مطابقت پذیری زوم بٹن۔"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"چھوٹی سے بڑی اسکرین پر زوم کریں۔"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"بلوٹوتھ مربوط ہے۔"</string>
@@ -823,9 +825,14 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"اسٹوریج"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"اشارات"</string>
<string name="instant_apps" msgid="6647570248119804907">"فوری ایپس"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"فوری ایپس کو انسٹالیشن کی ضرورت نہیں ہے۔"</string>
+ <!-- no translation found for instant_apps_title (8738419517367449783) -->
+ <skip />
+ <!-- no translation found for instant_apps_message (1183313016396018086) -->
+ <skip />
+ <!-- no translation found for instant_apps_message_with_help (6179830437630729747) -->
+ <skip />
<string name="app_info" msgid="6856026610594615344">"ایپ کی معلومات"</string>
- <string name="go_to_web" msgid="2650669128861626071">"براؤزر پر جائیں"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"ویب پر جائیں"</string>
<string name="mobile_data" msgid="7094582042819250762">"موبائل ڈیٹا"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi آف ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 2c45ab8..37d31c2 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"kamerani ochish"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Yangi vazifa tartibini tanlash"</string>
<string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yordam xabari"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"OK"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ilova belgisi"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Yordam xabari"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yuzingiz tekshirilmoqda…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Yuz belgisi"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kattalashtirish tugmasi mosligi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kattaroq ekran uchun kichikroqni kattalashtirish."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ulandi."</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Xotira"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Maslahatlar"</string>
<string name="instant_apps" msgid="6647570248119804907">"Darhol ochiladigan ilovalar"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Darhol ochiladigan ilovalarni o‘rnatish shart emas."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> ishlamoqda"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Ilova o‘rnatilmasdan ochildi."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Ilova o‘rnatilmasdan ochildi. Batafsil axborot oolish uchun bu yerga bosing."</string>
<string name="app_info" msgid="6856026610594615344">"Ilova haqida"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Brauzerni ochish"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Brauzerga o‘tish"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobil internet"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi o‘chiq"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8c5e10d..f476790 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"mở máy ảnh"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Chọn bố cục tác vụ mới"</string>
<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>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Biểu tượng ứng dụng"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Vùng thông báo trợ giúp"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Đang tìm kiếm bạn…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Biểu tượng khuôn mặt"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Đã kết nối bluetooth."</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Bộ nhớ"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Gợi ý"</string>
<string name="instant_apps" msgid="6647570248119804907">"Ứng dụng tức thì"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Ứng dụng tức thì không yêu cầu cài đặt."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> đang chạy"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Ứng dụng được mở mà không cần cài đặt."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Ứng dụng được mở mà không cần cài đặt. Nhấn để tìm hiểu thêm."</string>
<string name="app_info" msgid="6856026610594615344">"Thông tin ứng dụng"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Đi tới trình duyệt"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Truy cập web"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dữ liệu di động"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi tắt"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 6259bfd..aaa4c63 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"打开相机"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"选择新的任务布局"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"帮助消息区域"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"确认"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"请触摸指纹传感器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指纹图标"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"应用图标"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"帮助消息区域"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在查找中…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"面孔图标"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"蓝牙已连接。"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"存储空间"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"提示"</string>
<string name="instant_apps" msgid="6647570248119804907">"免安装应用"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"免安装应用无需安装就能使用。"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"正在运行<xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"已打开免安装应用。"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"已打开免安装应用。点按即可了解详情。"</string>
<string name="app_info" msgid="6856026610594615344">"应用信息"</string>
- <string name="go_to_web" msgid="2650669128861626071">"转到浏览器"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"转到网页版"</string>
<string name="mobile_data" msgid="7094582042819250762">"移动数据"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WLAN 已关闭"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index aa19f4f..aad4b4f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"開啟相機"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"選取新的工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"說明訊息區域"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"應用程式圖示"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"說明訊息區域"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在搜尋您的臉孔…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"面孔圖示"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"藍牙連線已建立。"</string>
@@ -825,9 +827,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"儲存空間"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"提示"</string>
<string name="instant_apps" msgid="6647570248119804907">"即時應用程式"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"即時應用程式無需安裝即可使用。"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> 運作中"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"已開啟免安裝應用程式。"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"已開啟免安裝應用程式。輕按即可瞭解詳情。"</string>
<string name="app_info" msgid="6856026610594615344">"應用程式資料"</string>
- <string name="go_to_web" msgid="2650669128861626071">"前往瀏覽器"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"前往網頁版"</string>
<string name="mobile_data" msgid="7094582042819250762">"流動數據"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi 已關閉"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 363c610..6b88545 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"開啟攝影機"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"選取新工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"說明訊息區域"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
- <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"應用程式圖示"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"說明訊息區域"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在尋找你的臉孔…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"臉孔圖示"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"藍牙連線已建立。"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"儲存空間"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"提示"</string>
<string name="instant_apps" msgid="6647570248119804907">"免安裝應用程式"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"免安裝應用程式不必安裝就能使用。"</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"正在執行「<xliff:g id="APP">%1$s</xliff:g>」"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"已開啟免安裝應用程式。"</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"已開啟免安裝應用程式。輕觸即可瞭解詳情。"</string>
<string name="app_info" msgid="6856026610594615344">"應用程式資訊"</string>
- <string name="go_to_web" msgid="2650669128861626071">"前往瀏覽器"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"前往網頁版"</string>
<string name="mobile_data" msgid="7094582042819250762">"行動數據"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi 已關閉"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 0e4729b..085c3b3 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -103,10 +103,12 @@
<string name="camera_label" msgid="7261107956054836961">"vula ikhamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Khetha isakhiwo somsebenzi omusha"</string>
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
+ <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Indawo yosizo lomlayezo"</string>
+ <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Qinisekisa"</string>
<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="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Isithonjana sohlelo lokusebenza"</string>
- <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Indawo yosizo lomlayezo"</string>
+ <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Kufunwa wena…"</string>
+ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Isithonjana sobuso"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Inkinobho evumelekile yokusondeza"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Sondeza kancane esikrinini esikhudlwana"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ixhunyiwe"</string>
@@ -823,9 +825,11 @@
<string name="notification_channel_storage" msgid="3077205683020695313">"Isitoreji"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Ukubonisa"</string>
<string name="instant_apps" msgid="6647570248119804907">"Izinhlelo zokusebenza ezisheshayo"</string>
- <string name="instant_apps_message" msgid="8116608994995104836">"Izinhlelo zokusebenza ezisheshayo azidingi ukufakwa."</string>
+ <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> esebenzayo"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"Uhlelo lokusebenza luvulwe ngaphndle kokufakwa."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"Uhlelo lokusebenza luvulwe ngaphandle kokufakwa. Thepha ukuze ufunde kabanzi."</string>
<string name="app_info" msgid="6856026610594615344">"Ulwazi lohlelo lokusebenza"</string>
- <string name="go_to_web" msgid="2650669128861626071">"Iya kusiphequluli"</string>
+ <string name="go_to_web" msgid="1106022723459948514">"Iya kuwebhu"</string>
<string name="mobile_data" msgid="7094582042819250762">"Idatha yeselula"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"I-Wi-Fi ivaliwe"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4920fb2..d1320a3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -160,13 +160,13 @@
<color name="smart_reply_button_background">#ffffffff</color>
<color name="smart_reply_button_stroke">#ffdadce0</color>
- <!-- Fingerprint dialog colors -->
- <color name="fingerprint_dialog_bg_color">#ffffffff</color> <!-- 100% white -->
- <color name="fingerprint_dialog_text_dark_color">#dd000000</color> <!-- 87% black -->
- <color name="fingerprint_dialog_text_light_color">#89000000</color> <!-- 54% black -->
- <color name="fingerprint_dialog_dim_color">#80000000</color> <!-- 50% black -->
- <color name="fingerprint_dialog_error_color">#fff44336</color> <!-- red -->
- <color name="fingerprint_dialog_fingerprint_color">#ff008577</color> <!-- teal -->
+ <!-- Biometric dialog colors -->
+ <color name="biometric_dialog_bg_color">#ffffffff</color> <!-- 100% white -->
+ <color name="biometric_dialog_text_dark_color">#dd000000</color> <!-- 87% black -->
+ <color name="biometric_dialog_text_light_color">#89000000</color> <!-- 54% black -->
+ <color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
+ <color name="biometric_dialog_error_color">#fff44336</color> <!-- red -->
+ <color name="biometric_dialog_biometric_color">#ff008577</color> <!-- teal -->
<!-- Logout button -->
<color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 11bd392..2fbf42f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<integer name="quick_settings_num_columns">3</integer>
<!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">1</integer>
+ <integer name="quick_settings_max_rows">3</integer>
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -146,19 +146,29 @@
<!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
<bool name="config_hideLtePlus">false</bool>
- <!-- milliseconds before the heads up notification auto-dismisses. -->
+ <!-- The number of milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
- <!-- milliseconds after a heads up notification is pushed back
+ <!-- The number of milliseconds after a heads up notification is pushed back
before the app can interrupt again. -->
<integer name="heads_up_default_snooze_length_ms">60000</integer>
<!-- Minimum display time for a heads up notification, in milliseconds. -->
<integer name="heads_up_notification_minimum_time">2000</integer>
- <!-- milliseconds before the heads up notification accepts touches. -->
+ <!-- The number of milliseconds before the heads up notification accepts touches. -->
<integer name="touch_acceptance_delay">700</integer>
+ <!-- The number of milliseconds before the ambient notification auto-dismisses. This will
+ override the default pulse length. -->
+ <integer name="ambient_notification_decay">6000</integer>
+
+ <!-- Minimum display time for a heads up notification, in milliseconds. -->
+ <integer name="ambient_notification_minimum_time">2000</integer>
+
+ <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
+ <integer name="ambient_notification_extension_time">6000</integer>
+
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
<integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
@@ -498,4 +508,7 @@
<!-- Allow dragging the PIP to a location to close it -->
<bool name="config_pipEnableDismissDragToEdge">true</bool>
+
+ <!-- SystemUI Plugins that can be loaded on user builds. -->
+ <string-array name="config_pluginWhitelist" translatable="false" />
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c168d4e..7c355c9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -650,7 +650,6 @@
<dimen name="keyguard_affordance_icon_width">24dp</dimen>
<dimen name="keyguard_indication_margin_bottom">65dp</dimen>
- <dimen name="keyguard_indication_margin_bottom_ambient">16dp</dimen>
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
@@ -939,9 +938,8 @@
burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_y">50dp</dimen>
- <!-- The maximum offset in either direction that the charging indication moves vertically
- to prevent burn-in on AOD. -->
- <dimen name="charging_indication_burn_in_prevention_offset_y">5dp</dimen>
+ <!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
+ <dimen name="default_burn_in_prevention_offset">5dp</dimen>
<dimen name="corner_size">8dp</dimen>
<dimen name="top_padding">0dp</dimen>
@@ -982,10 +980,10 @@
the regular notification, when we have remote input history texts present. -->
<dimen name="remote_input_history_extra_height">60dp</dimen>
- <!-- Fingerprint Dialog values -->
- <dimen name="fingerprint_dialog_fp_icon_size">64dp</dimen>
- <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
- <dimen name="fingerprint_dialog_corner_size">4dp</dimen>
+ <!-- Biometric Dialog values -->
+ <dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
+ <dimen name="biometric_dialog_corner_size">4dp</dimen>
+ <dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index 87c4bbb..fd7a105 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
<resources>
- <integer name="fingerprint_dialog_text_gravity">8388611</integer> <!-- gravity start -->
+ <integer name="biometric_dialog_text_gravity">8388611</integer> <!-- gravity start -->
<!-- Action footer width used for layout_width to indicate WRAP_CONTENT (along with a weight of
0) as we can allow the carrier text to stretch as far as needed in the QS footer. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 36f97cd..2b51aaa 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -257,14 +257,20 @@
<!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
<string name="cancel">Cancel</string>
+ <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <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>
+
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string>
- <!-- Content description of the application icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_fingerprint_dialog_app_icon">Application icon</string>
- <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_fingerprint_dialog_help_area">Help message area</string>
+
+ <!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] -->
+ <string name="face_dialog_looking_for_face">Looking for you\u2026</string>
+ <!-- Content description of the face icon when the system-provided face dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_face_dialog_face_icon">Face icon</string>
<!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
@@ -2132,14 +2138,23 @@
<!-- App label of the instant apps notification [CHAR LIMIT=60] -->
<string name="instant_apps">Instant Apps</string>
- <!-- Message of the instant apps notification indicating they don't need install [CHAR LIMIT=NONE] -->
- <string name="instant_apps_message">Instant apps don\'t require installation.</string>
+ <!-- Title of notification indicating that an instant app is running. [CHAR LIMIT=60] -->
+ <string name="instant_apps_title"><xliff:g id="app" example="Gmail">%1$s</xliff:g> running</string>
+
+ <!-- Message of the instant apps notification indicating they don't need install. [CHAR LIMIT=NONE] -->
+ <string name="instant_apps_message">App opened without being installed.</string>
+
+ <!-- Message of the instant apps notification indicating they don't need install, plus a link to more information. [CHAR LIMIT=NONE] -->
+ <string name="instant_apps_message_with_help">App opened without being installed. Tap to learn more.</string>
+
+ <!-- URL of the webpage that explains instant apps. -->
+ <string name="instant_apps_help_url" translatable="false"></string>
<!-- Action label for launching app info on the specified app [CHAR LIMIT=20] -->
<string name="app_info">App info</string>
<!-- Action label for switching to a browser for an instant app [CHAR LIMIT=20] -->
- <string name="go_to_web">Go to browser</string>
+ <string name="go_to_web">Go to web</string>
<!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] -->
<string name="mobile_data">Mobile data</string>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 710b5f7..defc49b 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -20,6 +20,10 @@
"src/**/I*.aidl",
],
+ static_libs: [
+ "SystemUIPluginLib"
+ ],
+
// Enforce that the library is build agains java 7 so that there are
// no compatibility issues with launcher
java_version: "1.7",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
new file mode 100644
index 0000000..9857894
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -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 com.android.systemui.shared.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * Provides necessary components for initializing {@link PluginManagerImpl}.
+ */
+public interface PluginInitializer {
+
+ Looper getBgLooper();
+
+ /**
+ * This Runnable is run on the bg looper during initialization of {@link PluginManagerImpl}.
+ * It can be null.
+ */
+ Runnable getBgInitCallback();
+
+ String[] getWhitelistedPlugins(Context context);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index d5541e9..e80c079 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.app.Notification;
import android.app.Notification.Action;
@@ -33,14 +33,19 @@
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginFragment;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class PluginInstanceManager<T extends Plugin> {
@@ -63,17 +68,18 @@
private final boolean isDebuggable;
private final PackageManager mPm;
private final PluginManagerImpl mManager;
+ private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
PluginInstanceManager(Context context, String action, PluginListener<T> listener,
boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
- manager, Build.IS_DEBUGGABLE);
+ manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins());
}
@VisibleForTesting
PluginInstanceManager(Context context, PackageManager pm, String action,
PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
- PluginManagerImpl manager, boolean debuggable) {
+ PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
mMainHandler = new MainHandler(Looper.getMainLooper());
mPluginHandler = new PluginHandler(looper);
mManager = manager;
@@ -83,6 +89,7 @@
mListener = listener;
mAllowMultiple = allowMultiple;
mVersion = version;
+ mWhitelistedPlugins.addAll(Arrays.asList(pluginWhitelist));
isDebuggable = debuggable;
}
@@ -108,7 +115,7 @@
public void destroy() {
if (DEBUG) Log.d(TAG, "stopListening");
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo plugin : plugins) {
mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
plugin.mPlugin).sendToTarget();
@@ -126,7 +133,7 @@
public boolean checkAndDisable(String className) {
boolean disableAny = false;
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo info : plugins) {
if (className.startsWith(info.mPackage)) {
disable(info);
@@ -137,7 +144,7 @@
}
public boolean disableAll() {
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (int i = 0; i < plugins.size(); i++) {
disable(plugins.get(i));
}
@@ -159,7 +166,7 @@
}
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo info : plugins) {
if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
return info.mVersion != null && info.mVersion.hasClass(cls);
@@ -294,9 +301,9 @@
protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
// This was already checked, but do it again here to make extra extra sure, we don't
// use these on production builds.
- if (!isDebuggable) {
+ if (!isDebuggable && !mWhitelistedPlugins.contains(component.getPackageName())) {
// Never ever ever allow these on production builds, they are only for prototyping.
- Log.d(TAG, "Somehow hit second debuggable check");
+ Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
return null;
}
String pkg = component.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
index 298eaf1..208f4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -12,10 +12,12 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.text.TextUtils;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
public interface PluginManager {
@@ -40,14 +42,17 @@
<T> boolean dependsOn(Plugin p, Class<T> cls);
- static <P> String getAction(Class<P> cls) {
- ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
- if (info == null) {
- throw new RuntimeException(cls + " doesn't provide an interface");
+ class Helper {
+ public static <P> String getAction(Class<P> cls) {
+ ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (info == null) {
+ throw new RuntimeException(cls + " doesn't provide an interface");
+ }
+ if (TextUtils.isEmpty(info.action())) {
+ throw new RuntimeException(cls + " doesn't provide an action");
+ }
+ return info.action();
}
- if (TextUtils.isEmpty(info.action())) {
- throw new RuntimeException(cls + " doesn't provide an action");
- }
- return info.action();
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 2a17e35..7f1d161 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.app.Notification;
import android.app.Notification.Action;
@@ -37,15 +37,15 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Log.TerribleFailure;
-import android.util.Log.TerribleFailureHandler;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import dalvik.system.PathClassLoader;
@@ -53,13 +53,14 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Arrays;
import java.util.Map;
-
/**
* @see Plugin
*/
public class PluginManagerImpl extends BroadcastReceiver implements PluginManager {
+ private static final String TAG = PluginManagerImpl.class.getSimpleName();
static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
private static PluginManager sInstance;
@@ -68,6 +69,7 @@
= new ArrayMap<>();
private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
private final ArraySet<String> mOneShotPackages = new ArraySet<>();
+ private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
private final Context mContext;
private final PluginInstanceManagerFactory mFactory;
private final boolean isDebuggable;
@@ -78,33 +80,35 @@
private Looper mLooper;
private boolean mWtfsSet;
- public PluginManagerImpl(Context context) {
- this(context, new PluginInstanceManagerFactory(),
- Build.IS_DEBUGGABLE, Thread.getUncaughtExceptionPreHandler());
+ public PluginManagerImpl(Context context, PluginInitializer initializer) {
+ this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
+ Thread.getUncaughtExceptionPreHandler(), initializer);
}
@VisibleForTesting
PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
- UncaughtExceptionHandler defaultHandler) {
+ UncaughtExceptionHandler defaultHandler, PluginInitializer initializer) {
mContext = context;
mFactory = factory;
- mLooper = Dependency.get(Dependency.BG_LOOPER);
+ mLooper = initializer.getBgLooper();
isDebuggable = debuggable;
+ mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
mPluginPrefs = new PluginPrefs(mContext);
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
defaultHandler);
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
- if (isDebuggable) {
- new Handler(mLooper).post(() -> {
- // Plugin dependencies that don't have another good home can go here, but
- // dependencies that have better places to init can happen elsewhere.
- Dependency.get(PluginDependencyProvider.class)
- .allowPluginDependency(ActivityStarter.class);
- });
+
+ Runnable bgRunnable = initializer.getBgInitCallback();
+ if (bgRunnable != null) {
+ new Handler(mLooper).post(bgRunnable);
}
}
+ public String[] getWhitelistedPlugins() {
+ return mWhitelistedPlugins.toArray(new String[0]);
+ }
+
public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
if (info == null) {
@@ -117,14 +121,12 @@
}
public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
- if (!isDebuggable) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- return null;
- }
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new RuntimeException("Must be called from UI thread");
}
- PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
+ // Passing null causes compiler to complain about incompatible (generic) types.
+ PluginListener<Plugin> dummy = null;
+ PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
false, mLooper, cls, this);
mPluginPrefs.addAction(action);
PluginInfo<T> info = p.getPlugin();
@@ -143,7 +145,7 @@
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
boolean allowMultiple) {
- addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
+ addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
}
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
@@ -153,10 +155,6 @@
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
Class cls, boolean allowMultiple) {
- if (!isDebuggable) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- return;
- }
mPluginPrefs.addAction(action);
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
allowMultiple, mLooper, cls, this);
@@ -166,10 +164,6 @@
}
public void removePluginListener(PluginListener<?> listener) {
- if (!isDebuggable) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- return;
- }
if (!mPluginMap.containsKey(listener)) return;
mPluginMap.remove(listener).destroy();
if (mPluginMap.size() == 0) {
@@ -261,6 +255,11 @@
}
public ClassLoader getClassLoader(String sourceDir, String pkg) {
+ if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) {
+ Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir +
+ ", pkg: " + pkg);
+ return null;
+ }
if (mClassLoaders.containsKey(pkg)) {
return mClassLoaders.get(pkg);
}
@@ -299,8 +298,12 @@
public void handleWtfs() {
if (!mWtfsSet) {
mWtfsSet = true;
- Log.setWtfHandler((tag, what, system) -> {
- throw new CrashWhilePluginActiveException(what);
+ Log.setWtfHandler(new Log.TerribleFailureHandler() {
+ @Override
+ public void onTerribleFailure(String tag, Log.TerribleFailure what,
+ boolean system) {
+ throw new CrashWhilePluginActiveException(what);
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
index 3671b3c..c0c5d70 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.content.Context;
import android.content.SharedPreferences;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
index facfd98..bb845cd 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
@@ -12,7 +12,9 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
+
+import android.util.ArrayMap;
import com.android.systemui.plugins.annotations.Dependencies;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -20,7 +22,7 @@
import com.android.systemui.plugins.annotations.Requirements;
import com.android.systemui.plugins.annotations.Requires;
-import android.util.ArrayMap;
+import java.util.function.BiConsumer;
public class VersionInfo {
@@ -73,25 +75,32 @@
}
public void checkVersion(VersionInfo plugin) throws InvalidVersionException {
- ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
- plugin.mVersions.forEach((aClass, version) -> {
- Version v = versions.remove(aClass);
- if (v == null) {
- v = createVersion(aClass);
- }
- if (v == null) {
- throw new InvalidVersionException(aClass.getSimpleName()
- + " does not provide an interface", false);
- }
- if (v.mVersion != version.mVersion) {
- throw new InvalidVersionException(aClass, v.mVersion < version.mVersion, v.mVersion,
- version.mVersion);
+ final ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
+ plugin.mVersions.forEach(new BiConsumer<Class<?>, Version>() {
+ @Override
+ public void accept(Class<?> aClass, Version version) {
+ Version v = versions.remove(aClass);
+ if (v == null) {
+ v = VersionInfo.this.createVersion(aClass);
+ }
+ if (v == null) {
+ throw new InvalidVersionException(aClass.getSimpleName()
+ + " does not provide an interface", false);
+ }
+ if (v.mVersion != version.mVersion) {
+ throw new InvalidVersionException(aClass, v.mVersion < version.mVersion,
+ v.mVersion,
+ version.mVersion);
+ }
}
});
- versions.forEach((aClass, version) -> {
- if (version.mRequired) {
- throw new InvalidVersionException("Missing required dependency "
- + aClass.getSimpleName(), false);
+ versions.forEach(new BiConsumer<Class<?>, Version>() {
+ @Override
+ public void accept(Class<?> aClass, Version version) {
+ if (version.mRequired) {
+ throw new InvalidVersionException("Missing required dependency "
+ + aClass.getSimpleName(), false);
+ }
}
});
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
index ab89043..e253360 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
@@ -21,13 +21,11 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
+import android.graphics.Picture;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-import android.view.ThreadedRenderer;
import android.view.View;
import java.util.function.Consumer;
@@ -108,12 +106,10 @@
* null if we were unable to allocate a hardware bitmap.
*/
public static Bitmap createHardwareBitmap(int width, int height, Consumer<Canvas> consumer) {
- RenderNode node = RenderNode.create("RecentsTransition", null);
- node.setLeftTopRightBottom(0, 0, width, height);
- node.setClipToBounds(false);
- DisplayListCanvas c = node.start(width, height);
- consumer.accept(c);
- node.end(c);
- return ThreadedRenderer.createHardwareBitmap(node, width, height);
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(width, height);
+ consumer.accept(canvas);
+ picture.endRecording();
+ return Bitmap.createBitmap(picture);
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index b04d047..c7910f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -342,6 +342,20 @@
}
/**
+ * Moves an already resumed task to the side of the screen to initiate split screen.
+ */
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
+ Rect initialBounds) {
+ try {
+ return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
+ createMode, true /* onTop */, false /* animate */, initialBounds,
+ true /* showRecents */);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 36fb3a7..7154f53 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -21,6 +21,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityOptions;
+import android.content.Context;
+import android.os.Handler;
/**
* Wrapper around internal ActivityOptions creation.
@@ -43,4 +45,17 @@
RemoteAnimationAdapterCompat remoteAnimationAdapter) {
return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
}
+
+ public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
+ int exitResId, final Runnable callback, final Handler callbackHandler) {
+ return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+ new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ if (callback != null) {
+ callbackHandler.post(callback);
+ }
+ }
+ });
+ }
}
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 d83b36d..3191d14 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
@@ -151,6 +151,25 @@
}
}
+ public void setPipVisibility(final boolean visible) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setPipVisibility(visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach window manager", e);
+ }
+ }
+
+ /**
+ * @return whether there is a soft nav bar.
+ */
+ public boolean hasSoftNavigationBar() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* @return The side of the screen where navigation bar is positioned.
* @see #NAV_BAR_POS_RIGHT
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5bbbc52..28eff46d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,7 +14,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 5ffdc7b..2daa33b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -377,6 +377,7 @@
@Override
public void onResume(int reason) {
+ displayDefaultSecurityMessage();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b159b39..b8df3c06 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -342,12 +342,11 @@
case SimPuk:
// Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode != SecurityMode.None
- || !mLockPatternUtils.isLockScreenDisabled(
+ if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
KeyguardUpdateMonitor.getCurrentUser())) {
- showSecurityScreen(securityMode);
- } else {
finish = true;
+ } else {
+ showSecurityScreen(securityMode);
}
break;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ff6a1c9..f1b53fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -39,9 +39,9 @@
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -49,11 +49,13 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
+import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -75,7 +77,6 @@
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants;
@@ -85,9 +86,8 @@
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-
+import com.android.systemui.shared.system.TaskStackChangeListener;
import com.google.android.collect.Lists;
import java.io.FileDescriptor;
@@ -250,6 +250,51 @@
private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
+ private class SettingObserver extends ContentObserver {
+ private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
+
+ private final ContentResolver mContentResolver;
+
+ /**
+ * Creates a content observer.
+ *
+ * @param handler The handler to run {@link #onChange} on, or null if none.
+ */
+ public SettingObserver(Handler handler) {
+ super(handler);
+ mContentResolver = mContext.getContentResolver();
+ updateContentObserver();
+ }
+
+ public void updateContentObserver() {
+ mContentResolver.unregisterContentObserver(this);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
+ false /* notifyForDescendents */,
+ this,
+ UserHandle.USER_CURRENT);
+
+ // Update the value immediately
+ onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
+ mFaceSettingEnabledForUser =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
+ 1 /* default */,
+ UserHandle.USER_CURRENT) != 0;
+ updateBiometricListeningState();
+ }
+ }
+ }
+
+ private final SettingObserver mSettingObserver;
+ private boolean mFaceSettingEnabledForUser;
+
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -1389,6 +1434,7 @@
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context);
+ mSettingObserver = new SettingObserver(mHandler);
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
@@ -1549,7 +1595,7 @@
(mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
&& !mSwitchingUser && !isFaceDisabled(getCurrentUser())
- && !mKeyguardGoingAway;
+ && !mKeyguardGoingAway && mFaceSettingEnabledForUser;
}
@@ -1719,6 +1765,7 @@
* Handle {@link #MSG_USER_SWITCH_COMPLETE}
*/
private void handleUserSwitchComplete(int userId) {
+ mSettingObserver.updateContentObserver();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2170,8 +2217,8 @@
}
}
- private final SysUiTaskStackChangeListener
- mTaskStackListener = new SysUiTaskStackChangeListener() {
+ private final TaskStackChangeListener
+ mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChangedBackground() {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index b2cf305..fe1fe1a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -39,9 +39,10 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
@@ -236,7 +237,7 @@
new DeviceProvisionedControllerImpl(mContext));
mProviders.put(PluginManager.class, () ->
- new PluginManagerImpl(mContext));
+ new PluginManagerImpl(mContext, new PluginInitializerImpl()));
mProviders.put(AssistManager.class, () ->
new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index bd2b7a5..1af2156 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -16,6 +16,11 @@
package com.android.systemui;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
+import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -33,11 +38,7 @@
import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
-
import com.android.systemui.OverviewProxyService.OverviewProxyListener;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -46,17 +47,11 @@
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
-
/**
* Class to send information from overview to launcher with a binder.
*/
@@ -133,7 +128,10 @@
}
long token = Binder.clearCallingIdentity();
try {
- EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
+ Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
+ if (divider != null) {
+ divider.onDockedFirstAnimationFrame();
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -314,8 +312,7 @@
getDefaultInteractionFlags());
// Listen for the package update changes.
- if (SystemServicesProxy.getInstance(context)
- .isSystemUser(mDeviceProvisionedController.getCurrentUser())) {
+ if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
updateEnabledState();
mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
index ddd4833..f6ad626 100644
--- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
@@ -21,7 +21,7 @@
import android.view.View;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.ViewProvider;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index f9dbf4a..fb343f9 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -22,27 +22,10 @@
public interface RecentsComponent {
void showRecentApps(boolean triggeredFromAltTab);
- void showNextAffiliatedTask();
- void showPrevAffiliatedTask();
/**
* Docks the top-most task and opens recents.
*/
- boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds,
+ boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
int metricsDockAction);
-
- /**
- * Called during a drag-from-navbar-in gesture.
- *
- * @param distanceFromTop the distance of the current drag in gesture from the top of the
- * screen
- */
- void onDraggingInRecents(float distanceFromTop);
-
- /**
- * Called when the gesture to drag in recents ended.
- *
- * @param velocity the velocity of the finger when releasing it in pixels per second
- */
- void onDraggingInRecentsEnded(float velocity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 044cc5c..48181bc 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -187,9 +187,11 @@
mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
mOverlay.setAlpha(0);
+ mOverlay.setAllowForceDark(false);
mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
mBottomOverlay.setAlpha(0);
+ mBottomOverlay.setAllowForceDark(false);
updateViews();
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a3b5395..0215fda 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -61,13 +61,14 @@
public static final float SWIPED_FAR_ENOUGH_SIZE_FRACTION = 0.6f;
static final float MAX_SCROLL_SIZE_FRACTION = 0.3f;
+ protected final Handler mHandler;
+
private float mMinSwipeProgress = 0f;
private float mMaxSwipeProgress = 1f;
private final FlingAnimationUtils mFlingAnimationUtils;
private float mPagingTouchSlop;
private final Callback mCallback;
- private final Handler mHandler;
private final int mSwipeDirection;
private final VelocityTracker mVelocityTracker;
private final FalsingManager mFalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index b96a604..78053b2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -34,7 +34,7 @@
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.util.NotificationChannels;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 04d72ce..258b6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -137,6 +138,7 @@
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
+ providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
providers.put(NotificationBlockingHelperManager.class,
() -> new NotificationBlockingHelperManager(context));
providers.put(NotificationRemoteInputManager.class,
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index bb82a54..8e29841 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -28,8 +28,8 @@
import java.io.PrintWriter;
import com.android.internal.os.BinderInternal;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
public class SystemUIService extends Service {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 8fe577a..8fc4689 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.os.Bundle;
@@ -31,9 +32,12 @@
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.CommandQueue;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
- * FingerprintDialogView).
+ * BiometricDialogView).
*/
public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
private static final String TAG = "BiometricDialogImpl";
@@ -48,7 +52,8 @@
private static final int MSG_USER_CANCELED = 7;
private static final int MSG_BUTTON_POSITIVE = 8;
- private FingerprintDialogView mDialogView;
+ private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
+ private BiometricDialogView mCurrentDialog;
private WindowManager mWindowManager;
private IBiometricPromptReceiver mReceiver;
private boolean mDialogShowing;
@@ -111,16 +116,25 @@
@Override
public void start() {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- return;
+ final PackageManager pm = mContext.getPackageManager();
+ mDialogs = new HashMap<>();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ mDialogs.put(BiometricAuthenticator.TYPE_FACE, new FaceDialogView(mContext, mCallback));
}
- getComponent(CommandQueue.class).addCallbacks(this);
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mDialogView = new FingerprintDialogView(mContext, mCallback);
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ mDialogs.put(BiometricAuthenticator.TYPE_FINGERPRINT,
+ new FingerprintDialogView(mContext, mCallback));
+ }
+
+ if (!mDialogs.isEmpty()) {
+ getComponent(CommandQueue.class).addCallbacks(this);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation) {
if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
// Remove these messages as they are part of the previous client
mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
@@ -129,6 +143,8 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
+ args.argi1 = type;
+ args.arg3 = requireConfirmation;
mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
}
@@ -157,33 +173,41 @@
}
private void handleShowDialog(SomeArgs args) {
+ final int type = args.argi1;
+ mCurrentDialog = mDialogs.get(type);
+
if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
- + mDialogView.isAnimatingAway());
- if (mDialogView.isAnimatingAway()) {
- mDialogView.forceRemove();
+ + mCurrentDialog.isAnimatingAway() + " type: " + type);
+
+ if (mCurrentDialog.isAnimatingAway()) {
+ mCurrentDialog.forceRemove();
} else if (mDialogShowing) {
Log.w(TAG, "Dialog already showing");
return;
}
mReceiver = (IBiometricPromptReceiver) args.arg2;
- mDialogView.setBundle((Bundle)args.arg1);
- mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
+ mCurrentDialog.setBundle((Bundle)args.arg1);
+ mCurrentDialog.setRequireConfirmation((boolean)args.arg3);
+ mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
mDialogShowing = true;
}
private void handleBiometricAuthenticated() {
if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
- // TODO: announce correct string depending on modality
- mDialogView.announceForAccessibility(
- mContext.getResources().getText(
- com.android.internal.R.string.fingerprint_authenticated));
- handleHideDialog(false /* userCanceled */);
+ mCurrentDialog.announceForAccessibility(
+ mContext.getResources()
+ .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
+ if (mCurrentDialog.requiresConfirmation()) {
+ mCurrentDialog.showConfirmationButton();
+ } else {
+ handleHideDialog(false /* userCanceled */);
+ }
}
private void handleBiometricHelp(String message) {
if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
- mDialogView.showHelpMessage(message);
+ mCurrentDialog.showHelpMessage(message);
}
private void handleBiometricError(String error) {
@@ -192,7 +216,7 @@
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
return;
}
- mDialogView.showErrorMessage(error);
+ mCurrentDialog.showErrorMessage(error);
}
private void handleHideDialog(boolean userCanceled) {
@@ -212,7 +236,7 @@
}
mReceiver = null;
mDialogShowing = false;
- mDialogView.startDismiss();
+ mCurrentDialog.startDismiss();
}
private void handleButtonNegative() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
new file mode 100644
index 0000000..c90861e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.biometrics;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+/**
+ * Abstract base class. Shows a dialog for BiometricPrompt.
+ */
+public abstract class BiometricDialogView extends LinearLayout {
+
+ private static final String TAG = "BiometricDialogView";
+
+ private static final int ANIMATION_DURATION_SHOW = 250; // ms
+ private static final int ANIMATION_DURATION_AWAY = 350; // ms
+
+ private static final int MSG_CLEAR_MESSAGE = 1;
+
+ protected static final int STATE_NONE = 0;
+ protected static final int STATE_AUTHENTICATING = 1;
+ protected static final int STATE_ERROR = 2;
+ protected static final int STATE_AUTHENTICATED = 3;
+
+ private final IBinder mWindowToken = new Binder();
+ private final Interpolator mLinearOutSlowIn;
+ private final WindowManager mWindowManager;
+ private final float mAnimationTranslationOffset;
+ private final int mErrorColor;
+ private final int mTextColor;
+ private final float mDisplayWidth;
+ private final DialogViewCallback mCallback;
+
+ private ViewGroup mLayout;
+ private final TextView mErrorText;
+ private Bundle mBundle;
+ private final LinearLayout mDialog;
+ private int mLastState;
+ private boolean mAnimatingAway;
+ private boolean mWasForceRemoved;
+ protected boolean mRequireConfirmation;
+
+ protected abstract void updateIcon(int lastState, int newState);
+ protected abstract int getHintStringResourceId();
+ protected abstract int getAuthenticatedAccessibilityResourceId();
+ protected abstract int getIconDescriptionResourceId();
+
+ private final Runnable mShowAnimationRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLayout.animate()
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_SHOW)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ mDialog.animate()
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION_SHOW)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
+ };
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_CLEAR_MESSAGE:
+ handleClearMessage();
+ break;
+ default:
+ Log.e(TAG, "Unhandled message: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ public BiometricDialogView(Context context, DialogViewCallback callback) {
+ super(context);
+ mCallback = callback;
+ mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mAnimationTranslationOffset = getResources()
+ .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
+ mErrorColor = Color.parseColor(
+ getResources().getString(R.color.biometric_dialog_error_color));
+ mTextColor = Color.parseColor(
+ getResources().getString(R.color.biometric_dialog_text_light_color));
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mWindowManager.getDefaultDisplay().getMetrics(metrics);
+ mDisplayWidth = metrics.widthPixels;
+
+ // Create the dialog
+ LayoutInflater factory = LayoutInflater.from(getContext());
+ mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false);
+ addView(mLayout);
+
+ mDialog = mLayout.findViewById(R.id.dialog);
+
+ mErrorText = mLayout.findViewById(R.id.error);
+
+ mLayout.setOnKeyListener(new View.OnKeyListener() {
+ boolean downPressed = false;
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode != KeyEvent.KEYCODE_BACK) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
+ downPressed = true;
+ } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ downPressed = false;
+ } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
+ downPressed = false;
+ mCallback.onUserCanceled();
+ }
+ return true;
+ }
+ });
+
+ final View space = mLayout.findViewById(R.id.space);
+ final View leftSpace = mLayout.findViewById(R.id.left_space);
+ final View rightSpace = mLayout.findViewById(R.id.right_space);
+ 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);
+
+ icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
+ mErrorText.setText(getResources().getString(getHintStringResourceId()));
+
+ setDismissesDialog(space);
+ setDismissesDialog(leftSpace);
+ setDismissesDialog(rightSpace);
+
+ negative.setOnClickListener((View v) -> {
+ mCallback.onNegativePressed();
+ });
+
+ positive.setOnClickListener((View v) -> {
+ mCallback.onPositivePressed();
+ });
+
+ mLayout.setFocusableInTouchMode(true);
+ mLayout.requestFocus();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ final TextView title = mLayout.findViewById(R.id.title);
+ 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);
+
+ mDialog.getLayoutParams().width = (int) mDisplayWidth;
+
+ mLastState = STATE_NONE;
+ updateState(STATE_AUTHENTICATING);
+
+ title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
+ title.setSelected(true);
+
+ positive.setVisibility(View.INVISIBLE);
+
+ final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
+ if (TextUtils.isEmpty(subtitleText)) {
+ subtitle.setVisibility(View.GONE);
+ } else {
+ subtitle.setVisibility(View.VISIBLE);
+ subtitle.setText(subtitleText);
+ }
+
+ final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+ if (TextUtils.isEmpty(descriptionText)) {
+ description.setVisibility(View.GONE);
+ } else {
+ description.setVisibility(View.VISIBLE);
+ description.setText(descriptionText);
+ }
+
+ negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+
+ if (!mWasForceRemoved) {
+ // Dim the background and slide the dialog up
+ mDialog.setTranslationY(mAnimationTranslationOffset);
+ mLayout.setAlpha(0f);
+ postOnAnimation(mShowAnimationRunnable);
+ } else {
+ // Show the dialog immediately
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mDialog.setAlpha(1.0f);
+ mDialog.setTranslationY(0);
+ mLayout.setAlpha(1.0f);
+ }
+ mWasForceRemoved = false;
+ }
+
+ private void setDismissesDialog(View v) {
+ v.setClickable(true);
+ v.setOnTouchListener((View view, MotionEvent event) -> {
+ mCallback.onUserCanceled();
+ return true;
+ });
+ }
+
+ public void startDismiss() {
+ mAnimatingAway = true;
+
+ final Runnable endActionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mWindowManager.removeView(BiometricDialogView.this);
+ mAnimatingAway = false;
+ }
+ };
+
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mLayout.animate()
+ .alpha(0f)
+ .setDuration(ANIMATION_DURATION_AWAY)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ mDialog.animate()
+ .translationY(mAnimationTranslationOffset)
+ .setDuration(ANIMATION_DURATION_AWAY)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .withEndAction(endActionRunnable)
+ .start();
+ }
+ });
+ }
+
+ /**
+ * Force remove the window, cancelling any animation that's happening. This should only be
+ * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+ * will cause the dialog to show without an animation the next time it's attached.
+ */
+ public void forceRemove() {
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mWindowManager.removeView(BiometricDialogView.this);
+ mAnimatingAway = false;
+ mWasForceRemoved = true;
+ }
+
+ public boolean isAnimatingAway() {
+ return mAnimatingAway;
+ }
+
+ public void setBundle(Bundle bundle) {
+ mBundle = bundle;
+ }
+
+ public void setRequireConfirmation(boolean requireConfirmation) {
+ mRequireConfirmation = requireConfirmation;
+ }
+
+ public boolean requiresConfirmation() {
+ return mRequireConfirmation;
+ }
+
+ public void showConfirmationButton() {
+ final Button positive = mLayout.findViewById(R.id.button1);
+ positive.setVisibility(View.VISIBLE);
+ }
+
+ public ViewGroup getLayout() {
+ return mLayout;
+ }
+
+ // Clears the temporary message and shows the help message.
+ private void handleClearMessage() {
+ updateState(STATE_AUTHENTICATING);
+ mErrorText.setText(getHintStringResourceId());
+ mErrorText.setTextColor(mTextColor);
+ }
+
+ // Shows an error/help message
+ private void showTemporaryMessage(String message) {
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+ updateState(STATE_ERROR);
+ mErrorText.setText(message);
+ mErrorText.setTextColor(mErrorColor);
+ mErrorText.setContentDescription(message);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
+ BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+
+ public void showHelpMessage(String message) {
+ showTemporaryMessage(message);
+ }
+
+ public void showErrorMessage(String error) {
+ showTemporaryMessage(error);
+ mCallback.onErrorShown();
+ }
+
+ private void updateState(int newState) {
+ updateIcon(mLastState, newState);
+ mLastState = newState;
+ }
+
+ public WindowManager.LayoutParams getLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("BiometricDialogView");
+ lp.token = mWindowToken;
+ return lp;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
new file mode 100644
index 0000000..feef3a6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.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.systemui.biometrics;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * This class loads the view for the system-provided dialog. The view consists of:
+ * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * and positive/negative buttons.
+ */
+public class FaceDialogView extends BiometricDialogView {
+ public FaceDialogView(Context context,
+ DialogViewCallback callback) {
+ super(context, callback);
+ }
+
+ @Override
+ protected int getHintStringResourceId() {
+ return R.string.face_dialog_looking_for_face;
+ }
+
+ @Override
+ protected int getAuthenticatedAccessibilityResourceId() {
+ if (mRequireConfirmation) {
+ return com.android.internal.R.string.face_authenticated_confirmation_required;
+ } else {
+ return com.android.internal.R.string.face_authenticated_no_confirmation_required;
+ }
+ }
+
+ @Override
+ protected int getIconDescriptionResourceId() {
+ return R.string.accessibility_face_dialog_face_icon;
+ }
+
+ @Override
+ protected void updateIcon(int lastState, int newState) {
+ Drawable icon = mContext.getDrawable(R.drawable.face_dialog_icon);
+
+ final ImageView faceIcon = getLayout().findViewById(R.id.biometric_icon);
+ faceIcon.setImageDrawable(icon);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 68c2c42..38a69a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -17,32 +17,11 @@
package com.android.systemui.biometrics;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
@@ -50,288 +29,27 @@
* Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
* and positive/negative buttons.
*/
-public class FingerprintDialogView extends LinearLayout {
-
+public class FingerprintDialogView extends BiometricDialogView {
private static final String TAG = "FingerprintDialogView";
- private static final int ANIMATION_DURATION_SHOW = 250; // ms
- private static final int ANIMATION_DURATION_AWAY = 350; // ms
-
- private static final int MSG_CLEAR_MESSAGE = 1;
-
- private static final int STATE_NONE = 0;
- private static final int STATE_FINGERPRINT = 1;
- private static final int STATE_FINGERPRINT_ERROR = 2;
- private static final int STATE_FINGERPRINT_AUTHENTICATED = 3;
-
- private final IBinder mWindowToken = new Binder();
- private final Interpolator mLinearOutSlowIn;
- private final WindowManager mWindowManager;
- private final float mAnimationTranslationOffset;
- private final int mErrorColor;
- private final int mTextColor;
- private final int mFingerprintColor;
- private final float mDisplayWidth;
- private final DialogViewCallback mCallback;
-
- private ViewGroup mLayout;
- private final TextView mErrorText;
- private Bundle mBundle;
- private final LinearLayout mDialog;
- private int mLastState;
- private boolean mAnimatingAway;
- private boolean mWasForceRemoved;
-
- private final Runnable mShowAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- }
- };
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_CLEAR_MESSAGE:
- handleClearMessage();
- break;
- default:
- Log.e(TAG, "Unhandled message: " + msg.what);
- break;
- }
- }
- };
-
- public FingerprintDialogView(Context context, DialogViewCallback callback) {
- super(context);
- mCallback = callback;
- mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mAnimationTranslationOffset = getResources()
- .getDimension(R.dimen.fingerprint_dialog_animation_translation_offset);
- mErrorColor = Color.parseColor(
- getResources().getString(R.color.fingerprint_dialog_error_color));
- mTextColor = Color.parseColor(
- getResources().getString(R.color.fingerprint_dialog_text_light_color));
- mFingerprintColor = Color.parseColor(
- getResources().getString(R.color.fingerprint_dialog_fingerprint_color));
-
- DisplayMetrics metrics = new DisplayMetrics();
- mWindowManager.getDefaultDisplay().getMetrics(metrics);
- mDisplayWidth = metrics.widthPixels;
-
- // Create the dialog
- LayoutInflater factory = LayoutInflater.from(getContext());
- mLayout = (ViewGroup) factory.inflate(R.layout.fingerprint_dialog, this, false);
- addView(mLayout);
-
- mDialog = mLayout.findViewById(R.id.dialog);
-
- mErrorText = mLayout.findViewById(R.id.error);
-
- mLayout.setOnKeyListener(new View.OnKeyListener() {
- boolean downPressed = false;
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_BACK) {
- return false;
- }
- if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
- downPressed = true;
- } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
- downPressed = false;
- } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
- downPressed = false;
- mCallback.onUserCanceled();
- }
- return true;
- }
- });
-
- final View space = mLayout.findViewById(R.id.space);
- final View leftSpace = mLayout.findViewById(R.id.left_space);
- final View rightSpace = mLayout.findViewById(R.id.right_space);
- final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
-
- setDismissesDialog(space);
- setDismissesDialog(leftSpace);
- setDismissesDialog(rightSpace);
-
- negative.setOnClickListener((View v) -> {
- mCallback.onNegativePressed();
- });
-
- positive.setOnClickListener((View v) -> {
- mCallback.onPositivePressed();
- });
-
- mLayout.setFocusableInTouchMode(true);
- mLayout.requestFocus();
+ @Override
+ protected int getHintStringResourceId() {
+ return R.string.fingerprint_dialog_touch_sensor;
}
@Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- final TextView title = mLayout.findViewById(R.id.title);
- 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);
-
- mDialog.getLayoutParams().width = (int) mDisplayWidth;
-
- mLastState = STATE_NONE;
- updateFingerprintIcon(STATE_FINGERPRINT);
-
- title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
- title.setSelected(true);
-
- final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
- if (TextUtils.isEmpty(subtitleText)) {
- subtitle.setVisibility(View.GONE);
- } else {
- subtitle.setVisibility(View.VISIBLE);
- subtitle.setText(subtitleText);
- }
-
- final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
- if (TextUtils.isEmpty(descriptionText)) {
- description.setVisibility(View.GONE);
- } else {
- description.setVisibility(View.VISIBLE);
- description.setText(descriptionText);
- }
-
- negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
-
- final CharSequence positiveText =
- mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT);
- positive.setText(positiveText); // needs to be set for marquee to work
- if (positiveText != null) {
- positive.setVisibility(View.VISIBLE);
- } else {
- positive.setVisibility(View.GONE);
- }
-
- if (!mWasForceRemoved) {
- // Dim the background and slide the dialog up
- mDialog.setTranslationY(mAnimationTranslationOffset);
- mLayout.setAlpha(0f);
- postOnAnimation(mShowAnimationRunnable);
- } else {
- // Show the dialog immediately
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mDialog.setAlpha(1.0f);
- mDialog.setTranslationY(0);
- mLayout.setAlpha(1.0f);
- }
- mWasForceRemoved = false;
+ protected int getAuthenticatedAccessibilityResourceId() {
+ return com.android.internal.R.string.fingerprint_authenticated;
}
- private void setDismissesDialog(View v) {
- v.setClickable(true);
- v.setOnTouchListener((View view, MotionEvent event) -> {
- mCallback.onUserCanceled();
- return true;
- });
+ @Override
+ protected int getIconDescriptionResourceId() {
+ return R.string.accessibility_fingerprint_dialog_fingerprint_icon;
}
- public void startDismiss() {
- mAnimatingAway = true;
-
- final Runnable endActionRunnable = new Runnable() {
- @Override
- public void run() {
- mWindowManager.removeView(FingerprintDialogView.this);
- mAnimatingAway = false;
- }
- };
-
- postOnAnimation(new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(0f)
- .setDuration(ANIMATION_DURATION_AWAY)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(mAnimationTranslationOffset)
- .setDuration(ANIMATION_DURATION_AWAY)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .withEndAction(endActionRunnable)
- .start();
- }
- });
- }
-
- /**
- * Force remove the window, cancelling any animation that's happening. This should only be
- * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
- * will cause the dialog to show without an animation the next time it's attached.
- */
- public void forceRemove() {
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mWindowManager.removeView(FingerprintDialogView.this);
- mAnimatingAway = false;
- mWasForceRemoved = true;
- }
-
- public boolean isAnimatingAway() {
- return mAnimatingAway;
- }
-
- public void setBundle(Bundle bundle) {
- mBundle = bundle;
- }
-
- // Clears the temporary message and shows the help message.
- private void handleClearMessage() {
- updateFingerprintIcon(STATE_FINGERPRINT);
- mErrorText.setText(R.string.fingerprint_dialog_touch_sensor);
- mErrorText.setTextColor(mTextColor);
- }
-
- // Shows an error/help message
- private void showTemporaryMessage(String message) {
- mHandler.removeMessages(MSG_CLEAR_MESSAGE);
- updateFingerprintIcon(STATE_FINGERPRINT_ERROR);
- mErrorText.setText(message);
- mErrorText.setTextColor(mErrorColor);
- mErrorText.setContentDescription(message);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
- BiometricPrompt.HIDE_DIALOG_DELAY);
- }
-
- public void showHelpMessage(String message) {
- showTemporaryMessage(message);
- }
-
- public void showErrorMessage(String error) {
- showTemporaryMessage(error);
- mCallback.onErrorShown();
- }
-
- private void updateFingerprintIcon(int newState) {
- Drawable icon = getAnimationForTransition(mLastState, newState);
+ @Override
+ protected void updateIcon(int lastState, int newState) {
+ Drawable icon = getAnimationForTransition(lastState, newState);
if (icon == null) {
Log.e(TAG, "Animation not found");
@@ -342,25 +60,28 @@
? (AnimatedVectorDrawable) icon
: null;
- final ImageView fingerprint_icon = mLayout.findViewById(R.id.fingerprint_icon);
- fingerprint_icon.setImageDrawable(icon);
+ final ImageView fingerprintIcon = getLayout().findViewById(R.id.biometric_icon);
+ fingerprintIcon.setImageDrawable(icon);
- if (animation != null && shouldAnimateForTransition(mLastState, newState)) {
+ if (animation != null && shouldAnimateForTransition(lastState, newState)) {
animation.forceAnimationOnUI();
animation.start();
}
+ }
- mLastState = newState;
+ public FingerprintDialogView(Context context,
+ DialogViewCallback callback) {
+ super(context, callback);
}
private boolean shouldAnimateForTransition(int oldState, int newState) {
- if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
+ if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
return false;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
return true;
- } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
+ } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
return true;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
// TODO(b/77328470): add animation when fingerprint is authenticated
return false;
}
@@ -369,32 +90,18 @@
private Drawable getAnimationForTransition(int oldState, int newState) {
int iconRes;
- if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
+ if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
+ } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
iconRes = R.drawable.fingerprint_dialog_error_to_fp;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
// TODO(b/77328470): add animation when fingerprint is authenticated
iconRes = R.drawable.fingerprint_dialog_error_to_fp;
- }
- else {
+ } else {
return null;
}
return mContext.getDrawable(iconRes);
}
-
- public WindowManager.LayoutParams getLayoutParams() {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("FingerprintDialogView");
- lp.token = mWindowToken;
- return lp;
- }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index d6a1cf0..5739c99 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.car;
import android.content.Context;
-import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -41,7 +40,7 @@
}
@Override
- public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by not pinning any
// notification if the shade is already opened.
@@ -49,6 +48,6 @@
return false;
}
- return super.shouldPeek(entry, sbn);
+ return super.shouldHeadsUp(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 6a29299..bb05980 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -48,7 +48,15 @@
void onIgnoreTouchWhilePulsing(boolean ignore);
interface Callback {
- default void onNotificationHeadsUp() {}
+ /**
+ * Called when a high priority notification is added.
+ */
+ default void onNotificationAlerted() {}
+
+ /**
+ * Called when battery state or power save mode changes.
+ * @param active whether power save is active or not
+ */
default void onPowerSaveChanged(boolean active) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index e6026c1..21b21d9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -44,7 +44,7 @@
public static final int PULSE_REASON_SENSOR_PICKUP = 3;
public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
- public static final int PULSE_REASON_SENSOR_REACH = 6;
+ public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 6;
public static final int REASON_SENSOR_WAKE_UP = 7;
private static boolean sRegisterKeyguardCallback = true;
@@ -177,9 +177,9 @@
log("state " + state);
}
- public static void traceReachWakeUp() {
+ public static void traceWakeLockScreenWakeUp() {
if (!ENABLED) return;
- log("reachWakeUp");
+ log("wakeLockScreenWakeUp");
}
public static void traceProximityResult(Context context, boolean near, long millis,
@@ -199,7 +199,7 @@
case PULSE_REASON_SENSOR_PICKUP: return "pickup";
case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
- case PULSE_REASON_SENSOR_REACH: return "reach";
+ case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakeLockScreen";
case REASON_SENSOR_WAKE_UP: return "wakeup";
default: throw new IllegalArgumentException("bad reason: " + pulseReason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 36b2347..c566460 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -16,11 +16,15 @@
package com.android.systemui.doze;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.Build;
import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
@@ -31,7 +35,12 @@
/**
* Controls the screen brightness when dozing.
*/
-public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListener {
+public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
+ SensorEventListener {
+ protected static final String ACTION_AOD_BRIGHTNESS =
+ "com.android.systemui.doze.AOD_BRIGHTNESS";
+ protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
+
private final Context mContext;
private final DozeMachine.Service mDozeService;
private final DozeHost mDozeHost;
@@ -40,36 +49,52 @@
private final Sensor mLightSensor;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
+ private final boolean mDebuggable;
private boolean mRegistered;
private int mDefaultDozeBrightness;
private boolean mPaused = false;
private int mLastSensorValue = -1;
+ /**
+ * Debug value used for emulating various display brightness buckets:
+ *
+ * {@code am broadcast -p com.android.systemui -a com.android.systemui.doze.AOD_BRIGHTNESS
+ * --ei brightness_bucket 1}
+ */
+ private int mDebugBrightnessBucket = -1;
+
+ @VisibleForTesting
public DozeScreenBrightness(Context context, DozeMachine.Service service,
SensorManager sensorManager, Sensor lightSensor, DozeHost host,
Handler handler, int defaultDozeBrightness, int[] sensorToBrightness,
- int[] sensorToScrimOpacity) {
+ int[] sensorToScrimOpacity, boolean debuggable) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
mLightSensor = lightSensor;
mDozeHost = host;
mHandler = handler;
+ mDebuggable = debuggable;
mDefaultDozeBrightness = defaultDozeBrightness;
mSensorToBrightness = sensorToBrightness;
mSensorToScrimOpacity = sensorToScrimOpacity;
+
+ if (mDebuggable) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_AOD_BRIGHTNESS);
+ mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
+ }
}
- @VisibleForTesting
public DozeScreenBrightness(Context context, DozeMachine.Service service,
SensorManager sensorManager, Sensor lightSensor, DozeHost host,
Handler handler, AlwaysOnDisplayPolicy policy) {
this(context, service, sensorManager, lightSensor, host, handler,
context.getResources().getInteger(
com.android.internal.R.integer.config_screenBrightnessDoze),
- policy.screenBrightnessArray, policy.dimmingScrimArray);
+ policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE);
}
@Override
@@ -87,7 +112,7 @@
resetBrightnessToDefault();
break;
case FINISH:
- setLightSensorEnabled(false);
+ onDestroy();
break;
}
if (newState != DozeMachine.State.FINISH) {
@@ -95,6 +120,13 @@
}
}
+ private void onDestroy() {
+ setLightSensorEnabled(false);
+ if (mDebuggable) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+
@Override
public void onSensorChanged(SensorEvent event) {
Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);
@@ -110,7 +142,9 @@
private void updateBrightnessAndReady() {
if (mRegistered) {
- int brightness = computeBrightness(mLastSensorValue);
+ int sensorValue = mDebugBrightnessBucket == -1
+ ? mLastSensorValue : mDebugBrightnessBucket;
+ int brightness = computeBrightness(sensorValue);
boolean brightnessReady = brightness > 0;
if (brightnessReady) {
mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness));
@@ -125,7 +159,7 @@
scrimOpacity = 255;
} else if (brightnessReady) {
// Only unblank scrim once brightness is ready.
- scrimOpacity = computeScrimOpacity(mLastSensorValue);
+ scrimOpacity = computeScrimOpacity(sensorValue);
}
if (scrimOpacity >= 0) {
mDozeHost.setAodDimmingScrim(scrimOpacity / 255f);
@@ -184,4 +218,9 @@
}
}
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
+ updateBrightnessAndReady();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index f9dfb5d..7013947 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -113,10 +113,10 @@
true /* reports touch coordinates */,
true /* touchscreen */),
new TriggerSensor(
- findSensorWithType(config.reachSensorType()),
- Settings.Secure.DOZE_REACH_GESTURE,
+ findSensorWithType(config.wakeLockScreenSensorType()),
+ Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
true /* configured */,
- DozeLog.PULSE_REASON_SENSOR_REACH,
+ DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */),
new WakeScreenSensor(),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 7339304..c61e10a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -26,7 +26,7 @@
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1589969..cb91d78 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -128,7 +128,7 @@
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 isReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_REACH;
+ boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
if (isLongPress) {
requestPulse(pulseReason, sensorPerformedProxCheck);
@@ -141,7 +141,7 @@
if (isDoubleTap) {
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
- } else if (isPickup || isReach) {
+ } else if (isPickup || isWakeLockScreen) {
mMachine.wakeUp();
} else {
mDozeHost.extendPulse();
@@ -156,8 +156,8 @@
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
- } else if (isReach) {
- DozeLog.traceReachWakeUp();
+ } else if (isWakeLockScreen) {
+ DozeLog.traceWakeLockScreenWakeUp();
}
}
@@ -409,7 +409,7 @@
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
- public void onNotificationHeadsUp() {
+ public void onNotificationAlerted() {
onNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
new file mode 100644
index 0000000..d1e5059
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
@@ -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.systemui.doze.util
+
+import android.util.MathUtils
+
+private const val MILLIS_PER_MINUTES = 1000 * 60f
+private const val BURN_IN_PREVENTION_PERIOD_Y = 521f
+private const val BURN_IN_PREVENTION_PERIOD_X = 83f
+
+/**
+ * Returns the translation offset that should be used to avoid burn in at
+ * the current time (in pixels.)
+ *
+ * @param amplitude Maximum translation that will be interpolated.
+ * @param xAxis If we're moving on X or Y.
+ */
+fun getBurnInOffset(amplitude: Int, xAxis: Boolean): Int {
+ return zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES,
+ amplitude.toFloat(),
+ if (xAxis) BURN_IN_PREVENTION_PERIOD_X else BURN_IN_PREVENTION_PERIOD_Y).toInt()
+}
+
+/**
+ * Implements a continuous, piecewise linear, periodic zig-zag function
+ *
+ * Can be thought of as a linear approximation of abs(sin(x)))
+ *
+ * @param period period of the function, ie. zigzag(x + period) == zigzag(x)
+ * @param amplitude maximum value of the function
+ * @return a value between 0 and amplitude
+ */
+private fun zigzag(x: Float, amplitude: Float, period: Float): Float {
+ val xprime = x % period / (period / 2)
+ val interpolationAmount = if (xprime <= 1) xprime else 2 - xprime
+ return MathUtils.lerp(0f, amplitude, interpolationAmount)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
index 745f312..d833c16 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
@@ -19,9 +19,6 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-
import java.util.ArrayList;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1655c01..4988f07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,9 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
+import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.READY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -30,7 +33,6 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -38,6 +40,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricSourceType;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
@@ -58,6 +61,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -70,21 +74,21 @@
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.internal.util.LatencyTracker;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -274,6 +278,12 @@
private KeyguardUpdateMonitor mUpdateMonitor;
+ /**
+ * Last SIM state reported by the telephony system.
+ * Index is the slotId - in case of multiple SIM cards.
+ */
+ private final SparseArray<IccCardConstants.State> mLastSimStates = new SparseArray<>();
+
private boolean mDeviceInteractive;
private boolean mGoingToSleep;
@@ -450,6 +460,14 @@
}
}
+ boolean simWasLocked;
+ synchronized (KeyguardViewMediator.this) {
+ IccCardConstants.State lastState = mLastSimStates.get(slotId);
+ simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
+ && simState == READY;
+ mLastSimStates.append(slotId, simState);
+ }
+
switch (simState) {
case NOT_READY:
case ABSENT:
@@ -503,6 +521,9 @@
case READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+ if (mShowing && simWasLocked) {
+ resetStateLocked();
+ }
mLockWhenSimRemoved = true;
}
break;
@@ -1917,6 +1938,12 @@
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
+
+ // TODO(b/113914868): investigation log for disappearing home button
+ Log.d(TAG, "adjustStatusBarLocked (b/113914868): mShowing=" + mShowing
+ + " mStatusBarManager=" + mStatusBarManager + " mOccluded="
+ + mOccluded + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons);
+
if (mStatusBarManager == null) {
Log.w(TAG, "Could not get status bar manager");
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 0cedf98..74f7706 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -19,7 +19,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.KeyguardManager;
import android.content.ComponentName;
@@ -29,11 +28,9 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
public class WorkLockActivityController {
private static final String TAG = WorkLockActivityController.class.getSimpleName();
@@ -111,7 +108,7 @@
}
}
- private final SysUiTaskStackChangeListener mLockListener = new SysUiTaskStackChangeListener() {
+ private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() {
@Override
public void onTaskProfileLocked(int taskId, int userId) {
startWorkChallengeInTask(taskId, userId);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index b7164cb..864a6f9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -21,11 +21,12 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-
+import android.os.UserHandle;
+import android.os.UserManager;
import com.android.systemui.SystemUI;
-import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.component.ExpandPipEvent;
import com.android.systemui.statusbar.CommandQueue;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -47,8 +48,8 @@
}
// Ensure that we are the primary user's SystemUI.
- final int processUser = SystemServicesProxy.getInstance(mContext).getProcessUser();
- if (!SystemServicesProxy.getInstance(mContext).isSystemUser(processUser)) {
+ final int processUser = UserManager.get(mContext).getUserHandle();
+ if (processUser != UserHandle.USER_SYSTEM) {
throw new IllegalStateException("Non-primary Pip component not currently supported.");
}
@@ -58,6 +59,7 @@
mPipManager.initialize(mContext);
getComponent(CommandQueue.class).addCallbacks(this);
+ putComponent(PipUI.class, this);
}
@Override
@@ -65,6 +67,10 @@
mPipManager.showPictureInPictureMenu();
}
+ public void expandPip() {
+ EventBus.getDefault().send(new ExpandPipEvent());
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java b/packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
rename to packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java
index 784ac4e..9bf46bb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents.misc;
+package com.android.systemui.pip.phone;
import android.os.Handler;
import android.os.HandlerThread;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 5547e2d..9ce2606 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -19,21 +19,17 @@
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
-
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
public class PipDismissViewController {
@@ -59,7 +55,7 @@
if (mDismissView == null) {
// Determine sizes for the view
final Rect stableInsets = new Rect();
- SystemServicesProxy.getInstance(mContext).getStableInsets(stableInsets);
+ WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
final Point windowSize = new Point();
mWindowManager.getDefaultDisplay().getRealSize(windowSize);
final int gradientHeight = mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index ee15655..04746c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -17,7 +17,6 @@
package com.android.systemui.pip.phone;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -36,15 +35,15 @@
import android.view.IPinnedStackListener;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
-
+import com.android.systemui.Dependency;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.component.ExpandPipEvent;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
-
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
/**
@@ -72,7 +71,7 @@
/**
* Handler for system task stack changes.
*/
- SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() {
+ TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
mTouchHandler.onActivityPinned();
@@ -80,7 +79,9 @@
mMenuController.onActivityPinned();
mAppOpsListener.onActivityPinned(packageName);
- SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ WindowManagerWrapper.getInstance().setPipVisibility(true);
+ });
}
@Override
@@ -93,7 +94,9 @@
mTouchHandler.onActivityUnpinned(topActivity);
mAppOpsListener.onActivityUnpinned();
- SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null);
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ WindowManagerWrapper.getInstance().setPipVisibility(topActivity != null);
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index f0ab046..ce7da79 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -30,7 +30,6 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ActivityManager.StackInfo;
-import android.app.ActivityTaskManager;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.Context;
@@ -43,14 +42,11 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.animation.Interpolator;
-
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.PipSnapAlgorithm;
-import com.android.systemui.recents.misc.ForegroundThread;
-import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FlingAnimationUtils;
-
import java.io.PrintWriter;
/**
@@ -116,7 +112,7 @@
*/
void onConfigurationChanged() {
mSnapAlgorithm.onConfigurationChanged();
- SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 020c550..43e9db7 100755
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -16,6 +16,11 @@
package com.android.systemui.pip.tv;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
@@ -44,22 +49,17 @@
import android.view.IPinnedStackListener;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
-
+import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.pip.BasePipManager;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.Display.DEFAULT_DISPLAY;
-
/**
* Manages the picture-in-picture (PIP) UI and states.
*/
@@ -630,7 +630,7 @@
return false;
}
- private SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() {
+ private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
@@ -754,7 +754,9 @@
}
private void updatePipVisibility(final boolean visible) {
- SystemServicesProxy.getInstance(mContext).setPipVisibility(visible);
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ WindowManagerWrapper.getInstance().setPipVisibility(visible);
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index c58d889..03daa95 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -18,6 +18,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.PluginDependency.DependencyProvider;
+import com.android.systemui.shared.plugins.PluginManager;
public class PluginDependencyProvider extends DependencyProvider {
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
new file mode 100644
index 0000000..108c2d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.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.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.shared.plugins.PluginInitializer;
+import com.android.systemui.R;
+
+public class PluginInitializerImpl implements PluginInitializer {
+ @Override
+ public Looper getBgLooper() {
+ return Dependency.get(Dependency.BG_LOOPER);
+ }
+
+ @Override
+ public Runnable getBgInitCallback() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ // Plugin dependencies that don't have another good home can go here, but
+ // dependencies that have better places to init can happen elsewhere.
+ Dependency.get(PluginDependencyProvider.class)
+ .allowPluginDependency(ActivityStarter.class);
+ }
+ };
+ }
+
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return context.getResources().getStringArray(R.array.config_pluginWhitelist);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 123fca7..4b5ab2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,20 +6,19 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
-import androidx.viewpager.widget.PagerAdapter;
-import androidx.viewpager.widget.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.Scroller;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
@@ -41,14 +40,12 @@
return t * t * t + 1.0f;
};
-
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TilePage> mPages = new ArrayList<>();
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
- private int mNumPages;
private PageListener mPageListener;
private boolean mListening;
@@ -56,6 +53,7 @@
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
+ private boolean mDistributeTiles = false;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -122,7 +120,7 @@
public void setPageIndicator(PageIndicator indicator) {
mPageIndicator = indicator;
- mPageIndicator.setNumPages(mNumPages);
+ mPageIndicator.setNumPages(mPages.size());
mPageIndicator.setLocation(mPageIndicatorPosition);
}
@@ -136,13 +134,15 @@
@Override
public void addTile(TileRecord tile) {
mTiles.add(tile);
- postDistributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
@Override
public void removeTile(TileRecord tile) {
if (mTiles.remove(tile)) {
- postDistributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
}
@@ -175,67 +175,94 @@
mPageListener = listener;
}
- private void postDistributeTiles() {
- removeCallbacks(mDistribute);
- post(mDistribute);
- }
-
private void distributeTiles() {
+ emptyAndInflateOrRemovePages();
+
+ final int tileCount = mPages.get(0).maxTiles();
if (DEBUG) Log.d(TAG, "Distributing tiles");
- final int NP = mPages.size();
- for (int i = 0; i < NP; i++) {
- mPages.get(i).removeAllViews();
- }
int index = 0;
final int NT = mTiles.size();
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
- if (mPages.get(index).isFull()) {
- if (++index == mPages.size()) {
- if (DEBUG) Log.d(TAG, "Adding page for "
- + tile.tile.getClass().getSimpleName());
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
- }
+ if (mPages.get(index).mRecords.size() == tileCount) index++;
+ if (DEBUG) {
+ Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+ + index);
}
- if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
- + index);
mPages.get(index).addTile(tile);
}
- if (mNumPages != index + 1) {
- mNumPages = index + 1;
- while (mPages.size() > mNumPages) {
- mPages.remove(mPages.size() - 1);
- }
- if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
- mPageIndicator.setNumPages(mNumPages);
- setAdapter(mAdapter);
- mAdapter.notifyDataSetChanged();
- setCurrentItem(0, false);
+ }
+
+ private void emptyAndInflateOrRemovePages() {
+ final int nTiles = mTiles.size();
+ int numPages = nTiles / mPages.get(0).maxTiles();
+ // Add one more not full page if needed
+ numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
+
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ mPages.get(i).removeAllViews();
}
+ if (NP == numPages) {
+ return;
+ }
+ while (mPages.size() < numPages) {
+ if (DEBUG) Log.d(TAG, "Adding page");
+ mPages.add((TilePage) LayoutInflater.from(getContext())
+ .inflate(R.layout.qs_paged_page, this, false));
+ }
+ while (mPages.size() > numPages) {
+ if (DEBUG) Log.d(TAG, "Removing page");
+ mPages.remove(mPages.size() - 1);
+ }
+ mPageIndicator.setNumPages(mPages.size());
+ setAdapter(mAdapter);
+ mAdapter.notifyDataSetChanged();
+ setCurrentItem(0, false);
}
@Override
public boolean updateResources() {
// Update bottom padding, useful for removing extra space once the panel page indicator is
// hidden.
- setPadding(0, 0, 0,
+ Resources res = getContext().getResources();
+ final int sidePadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+ setPadding(sidePadding, 0, sidePadding,
getContext().getResources().getDimensionPixelSize(
R.dimen.qs_paged_tile_layout_padding_bottom));
-
boolean changed = false;
for (int i = 0; i < mPages.size(); i++) {
changed |= mPages.get(i).updateResources();
}
if (changed) {
- distributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
return changed;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ final int nTiles = mTiles.size();
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+
+ // Only change the pages if the number of rows or columns (from updateResources) has
+ // changed or the tiles have changed
+ if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+ mDistributeTiles = false;
+ distributeTiles();
+ }
+
+ final int nRows = mPages.get(0).mRows;
+ for (int i = 0; i < mPages.size(); i++) {
+ TilePage t = mPages.get(i);
+ t.mRows = nRows;
+ }
+ }
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
// The ViewPager likes to eat all of the space, instead force it to wrap to the max height
// of the pages.
int maxHeight = 0;
@@ -249,13 +276,6 @@
setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom());
}
- private final Runnable mDistribute = new Runnable() {
- @Override
- public void run() {
- distributeTiles();
- }
- };
-
public int getColumnCount() {
if (mPages.size() == 0) return 0;
return mPages.get(0).mColumns;
@@ -346,33 +366,17 @@
};
public static class TilePage extends TileLayout {
- private int mMaxRows = 3;
+
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
- updateResources();
- }
-
- @Override
- public boolean updateResources() {
- final int rows = getRows();
- boolean changed = rows != mMaxRows;
- if (changed) {
- mMaxRows = rows;
- requestLayout();
- }
- return super.updateResources() || changed;
- }
-
- private int getRows() {
- return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows));
- }
-
- public void setMaxRows(int maxRows) {
- mMaxRows = maxRows;
}
public boolean isFull() {
- return mRecords.size() >= mColumns * mMaxRows;
+ return mRecords.size() >= mColumns * mRows;
+ }
+
+ public int maxTiles() {
+ return mColumns * mRows;
}
}
@@ -401,7 +405,7 @@
@Override
public int getCount() {
- return mNumPages;
+ return mPages.size();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index feff5d4..1451e71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
@@ -91,15 +92,24 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Since we control our own bottom, be whatever size we want.
- // Otherwise the QSPanel ends up with 0 height when the window is only the
- // size of the status bar.
- mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+ // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
+ // bottom and footer are inside the screen.
+ Configuration config = getResources().getConfiguration();
+ boolean navBelow = config.smallestScreenWidthDp >= 600
+ || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+
+ // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
+ // subtract its height. We do not care if the collapsed notifications fit in the screen.
+ int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+ - getPaddingBottom();
+ if (navBelow) {
+ maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+ }
+ mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanel.getMeasuredWidth();
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanel.getMeasuredHeight();
+ + mQSPanel.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index c36cdf6..79e5086 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -256,7 +256,7 @@
public void setExpanded(boolean expanded) {
if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
mQsExpanded = expanded;
- mQSPanel.setListening(mListening && mQsExpanded);
+ mQSPanel.setListening(mListening, mQsExpanded);
updateQsState();
}
@@ -287,8 +287,7 @@
mListening = listening;
mHeader.setListening(listening);
mFooter.setListening(listening);
- mQSPanel.setListening(mListening && mQsExpanded);
- mQSPanel.getFooter().setListening(listening);
+ mQSPanel.setListening(mListening, mQsExpanded);
}
@Override
@@ -365,7 +364,11 @@
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- getView().animate().setListener(null);
+ if (getView() != null) {
+ // The view could be destroyed before the animation completes when
+ // switching users.
+ getView().animate().setListener(null);
+ }
mHeaderAnimating = false;
updateQsState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7a57fdd..e98ef4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -199,7 +199,11 @@
public void openDetails(String subPanel) {
QSTile tile = getTile(subPanel);
- showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
+ // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
+ // QSFactory will not be able to create a tile and getTile will return null
+ if (tile != null) {
+ showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
+ }
}
private QSTile getTile(String subPanel) {
@@ -353,12 +357,21 @@
if (mListening) {
refreshAllTiles();
}
- if (mBrightnessView.getVisibility() == View.VISIBLE) {
- if (listening) {
- mBrightnessController.registerCallbacks();
- } else {
- mBrightnessController.unregisterCallbacks();
- }
+ }
+
+ public void setListening(boolean listening, boolean expanded) {
+ setListening(listening && expanded);
+ getFooter().setListening(listening);
+ // Set the listening as soon as the QS fragment starts listening regardless of the expansion,
+ // so it will update the current brightness before the slider is visible.
+ setBrightnessListening(listening);
+ }
+
+ public void setBrightnessListening(boolean listening) {
+ if (listening) {
+ mBrightnessController.registerCallbacks();
+ } else {
+ mBrightnessController.unregisterCallbacks();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 86e69e3..cefeeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -31,7 +31,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c67165e..e884302 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -24,10 +24,12 @@
protected int mCellMarginHorizontal;
protected int mCellMarginVertical;
protected int mSidePadding;
+ protected int mRows = 1;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mCellMarginTop;
private boolean mListening;
+ protected int mMaxAllowedRows = 3;
public TileLayout(Context context) {
this(context, null);
@@ -86,6 +88,7 @@
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
+ mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
if (mColumns != columns) {
mColumns = columns;
requestLayout();
@@ -96,10 +99,15 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
+ // it will show all its tiles. In this case, the tiles have to be entered before the
+ // container is measured. Any change in the tiles, should trigger a remeasure.
final int numTiles = mRecords.size();
- final int width = MeasureSpec.getSize(widthMeasureSpec)
- - getPaddingStart() - getPaddingEnd();
- final int numRows = (numTiles + mColumns - 1) / mColumns;
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ mRows = (numTiles + mColumns - 1) / mColumns;
+ }
mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
// Measure each QS tile.
@@ -112,13 +120,35 @@
// Only include the top margin in our measurement if we have more than 1 row to show.
// Otherwise, don't add the extra margin buffer at top.
- int height = (mCellHeight + mCellMarginVertical) * numRows +
- (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
+ int height = (mCellHeight + mCellMarginVertical) * mRows +
+ (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
if (height < 0) height = 0;
setMeasuredDimension(width, height);
}
+ /**
+ * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
+ * of 1 and a maximum of mMaxAllowedRows.
+ *
+ * @param heightMeasureSpec Available height.
+ * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
+ */
+ public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
+ final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop;
+ final int previousRows = mRows;
+ mRows = availableHeight / (mCellHeight + mCellMarginVertical);
+ if (mRows >= mMaxAllowedRows) {
+ mRows = mMaxAllowedRows;
+ } else if (mRows <= 1) {
+ mRows = 1;
+ }
+ if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+ mRows = (tilesCount + mColumns - 1) / mColumns;
+ }
+ return previousRows != mRows;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -135,7 +165,8 @@
int column = 0;
// Layout each QS tile.
- for (int i = 0; i < numRecords; i++, column++) {
+ final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
+ for (int i = 0; i < tilesToLayout; i++, column++) {
// If we reached the last column available to layout a tile, wrap back to the next row.
if (column == mColumns) {
column = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 3f7eeb8..dc17dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -69,6 +69,7 @@
private final QSDetailClipper mClipper;
private final LightBarController mLightBarController;
private final TileQueryHelper mTileQueryHelper;
+ private final View mTransparentView;
private boolean isShown;
private QSTileHost mHost;
@@ -108,6 +109,7 @@
mToolbar.getNavigationIcon().setTint(accentColor);
mToolbar.getOverflowIcon().setTint(accentColor);
mRecyclerView = findViewById(android.R.id.list);
+ mTransparentView = findViewById(R.id.customizer_transparent_view);
mTileAdapter = new TileAdapter(getContext());
mTileQueryHelper = new TileQueryHelper(context, mTileAdapter);
mRecyclerView.setAdapter(mTileAdapter);
@@ -127,6 +129,14 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateNavBackDrop(newConfig);
+ updateResources();
+ }
+
+ private void updateResources() {
+ LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
+ lp.height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
+ mTransparentView.setLayoutParams(lp);
}
private void updateNavBackDrop(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 12daff1..9edd65e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable;
import android.nfc.NfcAdapter;
import android.provider.Settings;
+import android.service.quicksettings.Tile;
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
@@ -77,6 +78,9 @@
@Override
protected void handleClick() {
+ if (getAdapter() == null) {
+ return;
+ }
if (!getAdapter().isEnabled()) {
getAdapter().enable();
} else {
@@ -96,13 +100,13 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
- final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
-
- if (getAdapter() == null) return;
- state.value = getAdapter().isEnabled();
+ state.value = getAdapter() != null && getAdapter().isEnabled();
+ state.state = getAdapter() == null
+ ? Tile.STATE_UNAVAILABLE
+ : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(
+ state.value ? R.drawable.ic_qs_nfc_enabled : R.drawable.ic_qs_nfc_disabled);
state.label = mContext.getString(R.string.quick_settings_nfc_label);
- state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
index fc1831d..90c1099 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -31,8 +31,7 @@
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecents(int recentsGrowTarget);
void onConfigurationChanged();
- void splitPrimaryTask(int topTaskId, int dragMode, int stackCreateMode,
- in Rect initialBounds);
+ void splitPrimaryTask(int topTaskId, int stackCreateMode, in Rect initialBounds);
void onDraggingInRecents(float distanceFromTop);
void onDraggingInRecentsEnded(float velocity);
void showCurrentUserToast(int msgResId, int msgLength);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index 58d8d8f..e977144 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -29,7 +29,7 @@
void updateRecentsVisibility(boolean visible);
void startScreenPinning(int taskId);
void sendRecentsDrawnEvent();
- void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
+ void sendDockingTopTaskEvent(in Rect initialRect);
void sendLaunchRecentsEvent();
void sendDockedFirstAnimationFrameEvent();
void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 8bb3c02..74f6c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -53,6 +53,7 @@
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.events.EventBus;
@@ -248,6 +249,10 @@
mImpl.onBootCompleted();
}
+ public void growRecents() {
+ EventBus.getDefault().send(new RecentsGrowingEvent());
+ }
+
/**
* Shows the Recents.
*/
@@ -463,7 +468,7 @@
}
@Override
- public boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds,
+ public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
int metricsDockAction) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
@@ -495,16 +500,15 @@
runningTask.topActivity.flattenToShortString());
}
if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.splitPrimaryTask(runningTask.id, dragMode, stackCreateMode,
- initialBounds);
+ mImpl.splitPrimaryTask(runningTask.id, stackCreateMode, initialBounds);
} else {
if (mSystemToUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
if (callbacks != null) {
try {
- callbacks.splitPrimaryTask(runningTask.id, dragMode,
- stackCreateMode, initialBounds);
+ callbacks.splitPrimaryTask(runningTask.id, stackCreateMode,
+ initialBounds);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
@@ -552,53 +556,6 @@
}
}
- @Override
- public void onDraggingInRecents(float distanceFromTop) {
- if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
- mImpl.onDraggingInRecents(distanceFromTop);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(
- mDraggingInRecentsCurrentUser);
- if (callbacks != null) {
- try {
- callbacks.onDraggingInRecents(distanceFromTop);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: "
- + mDraggingInRecentsCurrentUser);
- }
- }
- }
- }
-
- @Override
- public void onDraggingInRecentsEnded(float velocity) {
- if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
- mImpl.onDraggingInRecentsEnded(velocity);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(
- mDraggingInRecentsCurrentUser);
- if (callbacks != null) {
- try {
- callbacks.onDraggingInRecentsEnded(velocity);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: "
- + mDraggingInRecentsCurrentUser);
- }
- }
- }
- }
-
- @Override
public void showNextAffiliatedTask() {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
@@ -609,7 +566,6 @@
mImpl.showNextAffiliatedTask();
}
- @Override
public void showPrevAffiliatedTask() {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
@@ -686,7 +642,12 @@
public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
SystemServicesProxy ssp = Recents.getSystemServices();
int processUser = ssp.getProcessUser();
- if (!ssp.isSystemUser(processUser)) {
+ if (ssp.isSystemUser(processUser)) {
+ final Divider divider = getComponent(Divider.class);
+ if (divider != null) {
+ divider.onDockedFirstAnimationFrame();
+ }
+ } else {
postToSystemUser(new Runnable() {
@Override
public void run() {
@@ -723,7 +684,12 @@
public final void onBusEvent(final RecentsDrawnEvent event) {
int processUser = sSystemServicesProxy.getProcessUser();
- if (!sSystemServicesProxy.isSystemUser(processUser)) {
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
+ final Divider divider = getComponent(Divider.class);
+ if (divider != null) {
+ divider.onRecentsDrawn();
+ }
+ } else {
postToSystemUser(new Runnable() {
@Override
public void run() {
@@ -739,13 +705,17 @@
public final void onBusEvent(final DockedTopTaskEvent event) {
int processUser = sSystemServicesProxy.getProcessUser();
- if (!sSystemServicesProxy.isSystemUser(processUser)) {
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
+ final Divider divider = getComponent(Divider.class);
+ if (divider != null) {
+ divider.onDockedTopTask();
+ }
+ } else {
postToSystemUser(new Runnable() {
@Override
public void run() {
try {
- mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode,
- event.initialRect);
+ mUserToSystemCallbacks.sendDockingTopTaskEvent(event.initialRect);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
@@ -756,7 +726,12 @@
public final void onBusEvent(final RecentsActivityStartingEvent event) {
int processUser = sSystemServicesProxy.getProcessUser();
- if (!sSystemServicesProxy.isSystemUser(processUser)) {
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
+ final Divider divider = getComponent(Divider.class);
+ if (divider != null) {
+ divider.onRecentsActivityStarting();
+ }
+ } else {
postToSystemUser(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 63a65d0..d95c731 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -49,6 +49,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.pip.phone.ForegroundThread;
import com.google.android.collect.Lists;
import com.android.internal.logging.MetricsLogger;
@@ -72,7 +73,6 @@
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
@@ -690,14 +690,13 @@
showRelativeAffiliatedTask(false);
}
- public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode,
- Rect initialBounds) {
+ public void splitPrimaryTask(int taskId, int stackCreateMode, Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Make sure we inform DividerView before we actually start the activity so we can change
// the resize mode already.
if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
- EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
+ EventBus.getDefault().send(new DockedTopTaskEvent(initialBounds));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
index beec4b3..a1da785 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
@@ -87,12 +87,11 @@
}
@Override
- public void splitPrimaryTask(int topTaskId, int dragMode, int stackCreateMode,
- Rect initialBounds) throws RemoteException {
+ public void splitPrimaryTask(int topTaskId, int stackCreateMode, Rect initialBounds)
+ throws RemoteException {
SomeArgs args = SomeArgs.obtain();
args.argi1 = topTaskId;
- args.argi2 = dragMode;
- args.argi3 = stackCreateMode;
+ args.argi2 = stackCreateMode;
args.arg1 = initialBounds;
mHandler.sendMessage(mHandler.obtainMessage(MSG_DOCK_TOP_TASK, args));
}
@@ -141,7 +140,7 @@
break;
case MSG_DOCK_TOP_TASK:
args = (SomeArgs) msg.obj;
- mImpl.splitPrimaryTask(args.argi1, args.argi2, args.argi3 = 0,
+ mImpl.splitPrimaryTask(args.argi1, args.argi2 = 0,
(Rect) args.arg1);
break;
case MSG_ON_DRAGGING_IN_RECENTS:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index ff1f7dc..c5e9f04 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -26,13 +26,13 @@
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
+import com.android.systemui.pip.phone.ForegroundThread;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-import com.android.systemui.recents.misc.ForegroundThread;
/**
* An implementation of the system user's Recents interface to be called remotely by secondary
@@ -99,8 +99,8 @@
}
@Override
- public void sendDockingTopTaskEvent(int dragMode, Rect initialRect) throws RemoteException {
- EventBus.getDefault().post(new DockedTopTaskEvent(dragMode, initialRect));
+ public void sendDockingTopTaskEvent(Rect initialRect) throws RemoteException {
+ EventBus.getDefault().post(new DockedTopTaskEvent(initialRect));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
index f1bc214..9e3ced3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
@@ -26,11 +26,9 @@
*/
public class DockedTopTaskEvent extends EventBus.Event {
- public int dragMode;
public Rect initialRect;
- public DockedTopTaskEvent(int dragMode, Rect initialRect) {
- this.dragMode = dragMode;
+ public DockedTopTaskEvent(Rect initialRect) {
this.initialRect = initialRect;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 1178725..3ed5f70 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -460,7 +460,7 @@
private void animateSliderTo(int target) {
if (!mControlValueInitialized) {
- // Don't animate the first value since it's default state isn't meaningful to users.
+ // Don't animate the first value since its default state isn't meaningful to users.
mControl.setValue(target);
mControlValueInitialized = true;
}
@@ -470,10 +470,12 @@
mSliderAnimator = ValueAnimator.ofInt(mControl.getValue(), target);
mSliderAnimator.addUpdateListener((ValueAnimator animation) -> {
mExternalChange = true;
- mControl.setValue((int)animation.getAnimatedValue());
+ mControl.setValue((int) animation.getAnimatedValue());
mExternalChange = false;
});
- mSliderAnimator.setDuration(SLIDER_ANIMATION_DURATION);
+ final long animationDuration = SLIDER_ANIMATION_DURATION * Math.abs(
+ mControl.getValue() - target) / GAMMA_SPACE_MAX;
+ mSliderAnimator.setDuration(animationDuration);
mSliderAnimator.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 750002c..64fa8f8 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -18,11 +18,7 @@
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.os.UserHandle.USER_CURRENT;
-import static com.android.systemui.statusbar.phone.NavigationBarGestureHelper.DRAG_MODE_NONE;
-
-import android.app.ActivityManager;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.util.Log;
@@ -30,17 +26,11 @@
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.DividerView;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-
-import java.util.List;
/**
* Dispatches shortcut to System UI components
@@ -94,7 +84,7 @@
if (dockSide == WindowManager.DOCKED_INVALID) {
// Split the screen
Recents recents = getComponent(Recents.class);
- recents.splitPrimaryTask(DRAG_MODE_NONE, (shortcutCode == SC_DOCK_LEFT)
+ recents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
: SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index da0a435..ea194a7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -16,29 +16,28 @@
package com.android.systemui.stackdivider;
-import android.content.res.Configuration;
-import android.os.RemoteException;
-import android.view.IDockedStackListener;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IDockedStackListener;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* Controls the docked stack divider.
*/
-public class Divider extends SystemUI {
+public class Divider extends SystemUI implements DividerView.DividerCallbacks {
+ private static final String TAG = "Divider";
+
private DividerWindowManager mWindowManager;
private DividerView mView;
private final DividerState mDividerState = new DividerState();
@@ -55,10 +54,13 @@
update(mContext.getResources().getConfiguration());
putComponent(Divider.class, this);
mDockDividerVisibilityListener = new DockDividerVisibilityListener();
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.registerDockedStackListener(mDockDividerVisibilityListener);
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
+ mDockDividerVisibilityListener);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to register docked stack listener", e);
+ }
mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
- EventBus.getDefault().register(this);
}
@Override
@@ -82,7 +84,7 @@
private void addDivider(Configuration configuration) {
mView = (DividerView)
LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
- mView.injectDependencies(mWindowManager, mDividerState);
+ mView.injectDependencies(mWindowManager, mDividerState, this);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
final int size = mContext.getResources().getDimensionPixelSize(
@@ -156,18 +158,64 @@
mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
}
+ public void onRecentsActivityStarting() {
+ if (mView != null) {
+ mView.onRecentsActivityStarting();
+ }
+ }
+
/**
- * Workaround for b/62528361, at the time RecentsDrawnEvent is sent, it may happen before a
+ * Workaround for b/62528361, at the time recents has drawn, it may happen before a
* configuration change to the Divider, and internally, the event will be posted to the
* subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
* register the event handler here and proxy the event to the current DividerView.
*/
- public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
+ public void onRecentsDrawn() {
if (mView != null) {
mView.onRecentsDrawn();
}
}
+ public void onUndockingTask() {
+ if (mView != null) {
+ mView.onUndockingTask();
+ }
+ }
+
+ public void onDockedFirstAnimationFrame() {
+ if (mView != null) {
+ mView.onDockedFirstAnimationFrame();
+ }
+ }
+
+ public void onDockedTopTask() {
+ if (mView != null) {
+ mView.onDockedTopTask();
+ }
+ }
+
+ public void onAppTransitionFinished() {
+ mForcedResizableController.onAppTransitionFinished();
+ }
+
+ @Override
+ public void onDraggingStart() {
+ mForcedResizableController.onDraggingStart();
+ }
+
+ @Override
+ public void onDraggingEnd() {
+ mForcedResizableController.onDraggingEnd();
+ }
+
+ @Override
+ public void growRecents() {
+ Recents recents = getComponent(Recents.class);
+ if (recents != null) {
+ recents.growRecents();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mVisible="); pw.println(mVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 98925b9..fa01af6 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -55,7 +55,6 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
-
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm;
@@ -64,17 +63,8 @@
import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.activity.UndockingTaskEvent;
-import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.stackdivider.events.StartedDragingEvent;
-import com.android.systemui.stackdivider.events.StoppedDragingEvent;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
/**
* Docked stack divider.
@@ -82,6 +72,12 @@
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
+ public interface DividerCallbacks {
+ void onDraggingStart();
+ void onDraggingEnd();
+ void growRecents();
+ }
+
static final long TOUCH_ANIMATION_DURATION = 150;
static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
@@ -149,6 +145,7 @@
private FlingAnimationUtils mFlingAnimationUtils;
private DividerSnapAlgorithm mSnapAlgorithm;
private DividerSnapAlgorithm mMinimizedSnapAlgorithm;
+ private DividerCallbacks mCallback;
private final Rect mStableInsets = new Rect();
private boolean mGrowRecents;
@@ -162,6 +159,7 @@
private DividerState mState;
private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
+
// The view is removed or in the process of been removed from the system.
private boolean mRemoved;
@@ -306,7 +304,6 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- EventBus.getDefault().register(this);
// Save the current target if not minimized once attached to window
if (mHomeStackResizable && mDockSide != WindowManager.DOCKED_INVALID
@@ -315,14 +312,9 @@
}
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(this);
- }
-
void onDividerRemoved() {
mRemoved = true;
+ mCallback = null;
mHandler.removeMessages(MSG_RESIZE_STACK);
}
@@ -364,13 +356,15 @@
}
}
- public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState) {
+ public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
+ DividerCallbacks callback) {
mWindowManager = windowManager;
mState = dividerState;
+ mCallback = callback;
// Set the previous position ratio before minimized state after attaching this divider
if (mStableInsets.isEmpty()) {
- SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
}
if (mState.mRatioPositionBeforeMinimized == 0) {
@@ -419,7 +413,9 @@
mWindowManager.setSlippery(false);
liftBackground();
}
- EventBus.getDefault().send(new StartedDragingEvent());
+ if (mCallback != null) {
+ mCallback.onDraggingStart();
+ }
return mDockSide != WindowManager.DOCKED_INVALID;
}
@@ -617,7 +613,9 @@
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
- EventBus.getDefault().send(new StoppedDragingEvent());
+ if (mCallback != null) {
+ mCallback.onDraggingEnd();
+ }
// Record last snap target the divider moved to
if (mHomeStackResizable && !mIsInMinimizeInteraction) {
@@ -776,7 +774,7 @@
if (mDisplayRotation != mDefaultDisplay.getRotation()) {
// Splitscreen to minimize is about to starts after rotating landscape to seascape,
// update insets, display info and snap algorithm targets
- SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
repositionSnapTargetBeforeMinimized();
updateDisplayInfo();
} else {
@@ -910,7 +908,7 @@
requestLayout();
// Update the snap position to the new docked side with correct insets
- SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
mMinimizedSnapAlgorithm = null;
initializeSnapAlgorithm();
@@ -1271,7 +1269,7 @@
}
}
- public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) {
+ void onRecentsActivityStarting() {
if (mGrowRecents && mDockSide == WindowManager.DOCKED_TOP
&& getSnapAlgorithm().getMiddleTarget() != getSnapAlgorithm().getLastSplitTarget()
&& getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) {
@@ -1280,16 +1278,14 @@
}
}
- public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+ void onDockedFirstAnimationFrame() {
saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
}
- public final void onBusEvent(DockedTopTaskEvent event) {
- if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
- mState.growAfterRecentsDrawn = false;
- mState.animateAfterRecentsDrawn = true;
- startDragging(false /* animate */, false /* touching */);
- }
+ void onDockedTopTask() {
+ mState.growAfterRecentsDrawn = false;
+ mState.animateAfterRecentsDrawn = true;
+ startDragging(false /* animate */, false /* touching */);
updateDockSide();
mEntranceAnimationRunning = true;
@@ -1297,7 +1293,7 @@
mSnapAlgorithm.getMiddleTarget());
}
- public void onRecentsDrawn() {
+ void onRecentsDrawn() {
updateDockSide();
final int position = calculatePositionForInsetBounds();
if (mState.animateAfterRecentsDrawn) {
@@ -1314,13 +1310,15 @@
if (mState.growAfterRecentsDrawn) {
mState.growAfterRecentsDrawn = false;
updateDockSide();
- EventBus.getDefault().send(new RecentsGrowingEvent());
+ if (mCallback != null) {
+ mCallback.growRecents();
+ }
stopDragging(position, getSnapAlgorithm().getMiddleTarget(), 336,
Interpolators.FAST_OUT_SLOW_IN);
}
}
- public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) {
+ void onUndockingTask() {
int dockSide = mWindowManagerProxy.getDockSide();
if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable
|| !mDockedStackMinimized)) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
index 4415bd7..02f7505 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
@@ -52,7 +52,7 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.forced_resizable_activity);
- TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
+ TextView tv = findViewById(com.android.internal.R.id.message);
int reason = getIntent().getIntExtra(EXTRA_FORCED_RESIZEABLE_REASON, -1);
String text;
switch (reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index 826fa6c..f66db48 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -16,8 +16,7 @@
package com.android.systemui.stackdivider;
-import static com.android.systemui.stackdivider.ForcedResizableInfoActivity
- .EXTRA_FORCED_RESIZEABLE_REASON;
+import static com.android.systemui.stackdivider.ForcedResizableInfoActivity.EXTRA_FORCED_RESIZEABLE_REASON;
import android.app.ActivityOptions;
import android.content.Context;
@@ -26,16 +25,9 @@
import android.os.UserHandle;
import android.util.ArraySet;
import android.widget.Toast;
-
import com.android.systemui.R;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
-import com.android.systemui.recents.events.component.ShowUserToastEvent;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.events.StartedDragingEvent;
-import com.android.systemui.stackdivider.events.StoppedDragingEvent;
+import com.android.systemui.shared.system.TaskStackChangeListener;
/**
* Controller that decides when to show the {@link ForcedResizableInfoActivity}.
@@ -49,7 +41,7 @@
private final Handler mHandler = new Handler();
private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>();
private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
- private boolean mDividerDraging;
+ private boolean mDividerDragging;
private final Runnable mTimeoutRunnable = new Runnable() {
@Override
@@ -75,9 +67,8 @@
public ForcedResizableInfoActivityController(Context context) {
mContext = context;
- EventBus.getDefault().register(this);
ActivityManagerWrapper.getInstance().registerTaskStackListener(
- new SysUiTaskStackChangeListener() {
+ new TaskStackChangeListener() {
@Override
public void onActivityForcedResizable(String packageName, int taskId,
int reason) {
@@ -102,19 +93,19 @@
}
}
- public final void onBusEvent(AppTransitionFinishedEvent event) {
- if (!mDividerDraging) {
+ public void onAppTransitionFinished() {
+ if (!mDividerDragging) {
showPending();
}
}
- public final void onBusEvent(StartedDragingEvent event) {
- mDividerDraging = true;
+ void onDraggingStart() {
+ mDividerDragging = true;
mHandler.removeCallbacks(mTimeoutRunnable);
}
- public final void onBusEvent(StoppedDragingEvent event) {
- mDividerDraging = false;
+ void onDraggingEnd() {
+ mDividerDragging = false;
showPending();
}
@@ -127,13 +118,13 @@
}
private void activityDismissingDockedStack() {
- EventBus.getDefault().send(new ShowUserToastEvent(
- R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
+ Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
+ Toast.LENGTH_SHORT).show();
}
private void activityLaunchOnSecondaryDisplayFailed() {
- EventBus.getDefault().send(new ShowUserToastEvent(
- R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT));
+ Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text,
+ Toast.LENGTH_SHORT).show();
}
private void showPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 4516518..b6e88d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -165,7 +165,7 @@
* Whether or not the given notification is alerting and managed by this manager.
* @return true if the notification is alerting
*/
- public boolean contains(@NonNull String key) {
+ public boolean isAlerting(@NonNull String key) {
return mAlertEntries.containsKey(key);
}
@@ -294,7 +294,7 @@
removeAutoRemovalCallbacks();
if (!isSticky()) {
- long finishTime = mPostTime + mAutoDismissNotificationDecay;
+ long finishTime = calculateFinishTime();
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveAlertRunnable, removeDelay);
}
@@ -357,6 +357,14 @@
protected long calculatePostTime() {
return mClock.currentTimeMillis();
}
+
+ /**
+ * Calculate when the notification should auto-dismiss itself.
+ * @return the finish time
+ */
+ protected long calculateFinishTime() {
+ return mPostTime + mAutoDismissNotificationDecay;
+ }
}
protected final static class Clock {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
new file mode 100644
index 0000000..2c384d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+
+/**
+ * Manager which handles high priority notifications that should "pulse" in when the device is
+ * 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 {
+
+ protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
+ @VisibleForTesting
+ protected long mExtensionTime;
+
+ public AmbientPulseManager(@NonNull final Context context) {
+ Resources resources = context.getResources();
+ mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
+ mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time);
+ mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
+ }
+
+ /**
+ * Adds an OnAmbientChangedListener to observe events.
+ */
+ public void addListener(@NonNull OnAmbientChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes the OnAmbientChangedListener from the observer list.
+ */
+ public void removeListener(@NonNull OnAmbientChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
+ * longer.
+ */
+ public void extendPulse() {
+ AmbientEntry topEntry = getTopEntry();
+ if (topEntry == null) {
+ return;
+ }
+ topEntry.extendPulse();
+ }
+
+ @Override
+ protected void onAlertEntryAdded(AlertEntry alertEntry) {
+ NotificationData.Entry entry = alertEntry.mEntry;
+ entry.row.setAmbientPulsing(true);
+ for (OnAmbientChangedListener listener : mListeners) {
+ listener.onAmbientStateChanged(entry, true);
+ }
+ }
+
+ @Override
+ protected void onAlertEntryRemoved(AlertEntry alertEntry) {
+ NotificationData.Entry entry = alertEntry.mEntry;
+ entry.row.setAmbientPulsing(false);
+ for (OnAmbientChangedListener listener : mListeners) {
+ listener.onAmbientStateChanged(entry, false);
+ }
+ }
+
+ @Override
+ protected AlertEntry createAlertEntry() {
+ return new AmbientEntry();
+ }
+
+ /**
+ * Get the top pulsing entry. This should be the currently showing one if there are multiple.
+ * @return the currently showing entry
+ */
+ private AmbientEntry getTopEntry() {
+ if (mAlertEntries.isEmpty()) {
+ return null;
+ }
+ AlertEntry topEntry = null;
+ for (AlertEntry entry : mAlertEntries.values()) {
+ if (topEntry == null || entry.compareTo(topEntry) < 0) {
+ topEntry = entry;
+ }
+ }
+ return (AmbientEntry) topEntry;
+ }
+
+ /**
+ * Observer interface for any changes in the ambient entries.
+ */
+ public interface OnAmbientChangedListener {
+ /**
+ * Called when an entry starts or stops pulsing.
+ * @param entry the entry that changed
+ * @param isPulsing true if the entry is now pulsing, false otherwise
+ */
+ void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+ }
+
+ private final class AmbientEntry extends AlertEntry {
+ private boolean extended;
+
+ /**
+ * Extend the lifetime of the alertEntry so that it auto-removes later. Can only be
+ * extended once.
+ */
+ private void extendPulse() {
+ if (!extended) {
+ extended = true;
+ updateEntry(false);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ extended = false;
+ }
+
+ @Override
+ protected long calculateFinishTime() {
+ return super.calculateFinishTime() + (extended ? mExtensionTime : 0);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e19c844..5c0b328 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -161,7 +161,7 @@
default void onRotationProposal(int rotation, boolean isValid) { }
default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
- int type) { }
+ int type, boolean requireConfirmation) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
@@ -514,12 +514,14 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
args.argi1 = type;
+ args.arg3 = requireConfirmation;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
@@ -763,7 +765,8 @@
mCallbacks.get(i).showBiometricDialog(
(Bundle) someArgs.arg1,
(IBiometricPromptReceiver) someArgs.arg2,
- someArgs.argi1);
+ someArgs.argi1,
+ (boolean) someArgs.arg3);
}
someArgs.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 247e3d3..00e0b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar;
+import static android.content.Context.LAYOUT_INFLATER_SERVICE;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlertDialog;
@@ -30,10 +34,10 @@
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
-import android.graphics.drawable.Icon;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
@@ -51,29 +55,23 @@
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowManager;
import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
-
import com.android.internal.app.AssistUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import static android.content.Context.LAYOUT_INFLATER_SERVICE;
-import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-
/**
* Contains functionality for handling keyboard shortcuts.
*/
@@ -372,19 +370,19 @@
private void showKeyboardShortcuts(int deviceId) {
retrieveKeyCharacterMap(deviceId);
- SystemServicesProxy.getInstance(mContext).requestKeyboardShortcuts(mContext,
- new KeyboardShortcutsReceiver() {
- @Override
- public void onKeyboardShortcutsReceived(
- final List<KeyboardShortcutGroup> result) {
- result.add(getSystemShortcuts());
- final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
- if (appShortcuts != null) {
- result.add(appShortcuts);
- }
- showKeyboardShortcutsDialog(result);
- }
- }, deviceId);
+ WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ wm.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
+ @Override
+ public void onKeyboardShortcutsReceived(
+ final List<KeyboardShortcutGroup> result) {
+ result.add(getSystemShortcuts());
+ final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
+ if (appShortcuts != null) {
+ result.add(appShortcuts);
+ }
+ showKeyboardShortcutsDialog(result);
+ }
+ }, deviceId);
}
private void dismissKeyboardShortcuts() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 551e8a9..0c5f391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -298,27 +298,6 @@
if (mVisible) {
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
- if (mDozing) {
- mTextView.setTextColor(Color.WHITE);
- if (!TextUtils.isEmpty(mTransientIndication)) {
- // When dozing we ignore any text color and use white instead, because
- // colors can be hard to read in low brightness.
- mTextView.switchIndication(mTransientIndication);
- } else if (mPowerPluggedIn) {
- String indication = computePowerIndication();
- if (animate) {
- animateText(mTextView, indication);
- } else {
- mTextView.switchIndication(indication);
- }
- } else {
- String percentage = NumberFormat.getPercentInstance()
- .format(mBatteryLevel / 100f);
- mTextView.switchIndication(percentage);
- }
- return;
- }
-
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustGrantedIndication = getTrustGrantedIndication();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index d6886f5..89a842e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -37,12 +38,15 @@
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.Dumpable;
import com.android.systemui.OverviewProxyService;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
@@ -52,7 +56,7 @@
* Handles keeping track of the current user, profiles, and various things related to hiding
* contents, redacting notifications, and the lockscreen.
*/
-public class NotificationLockscreenUserManager implements Dumpable {
+public class NotificationLockscreenUserManager implements Dumpable, StateListener {
private static final String TAG = "LockscreenUserManager";
private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
@@ -67,9 +71,13 @@
Dependency.get(DeviceProvisionedController.class);
private final UserManager mUserManager;
private final IStatusBarService mBarService;
+ private final LockPatternUtils mLockPatternUtils;
+ private final KeyguardManager mKeyguardManager;
+ private StatusBarKeyguardViewManager mKeyguardViewManager;
private boolean mShowLockscreenNotifications;
private boolean mAllowLockscreenRemoteInput;
+ private int mState = StatusBarState.SHADE;
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override
@@ -84,7 +92,9 @@
mEntryManager.updateNotifications();
} else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
if (userId != mCurrentUserId && isCurrentProfile(userId)) {
+ updatePublicMode();
mPresenter.onWorkChallengeChanged();
+ mEntryManager.updateNotifications();
}
}
}
@@ -100,8 +110,9 @@
Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
updateLockscreenNotificationSetting();
-
+ updatePublicMode();
mPresenter.onUserSwitched(mCurrentUserId);
+ mEntryManager.getNotificationData().filterAndSort();
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
updateCurrentProfilesCache();
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
@@ -150,6 +161,9 @@
mCurrentUserId = ActivityManager.getCurrentUser();
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ Dependency.get(StatusBarStateController.class).addListener(this);
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -239,6 +253,43 @@
}
}
+ public void setKeyguardViewManager(StatusBarKeyguardViewManager sbkvm) {
+ mKeyguardViewManager = sbkvm;
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ mState = newState;
+ updatePublicMode();
+ }
+
+ public void updatePublicMode() {
+ //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
+ // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
+ // asking if the keyguard is showing. We still need to check it though because showing the
+ // camera on the keyguard has a state of SHADE but the keyguard is still showing.
+ boolean showingKeyguard = mState != StatusBarState.SHADE
+ || mKeyguardViewManager.isShowing();
+ boolean devicePublic = showingKeyguard && mKeyguardViewManager.isSecure(getCurrentUserId());
+
+ SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
+ for (int i = currentProfiles.size() - 1; i >= 0; i--) {
+ final int userId = currentProfiles.valueAt(i).id;
+ boolean isProfilePublic = devicePublic;
+ if (!devicePublic && userId != mCurrentUserId) {
+ // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
+ // due to a race condition where this code could be called before
+ // TrustManagerService updates its internal records, resulting in an incorrect
+ // state being cached in mLockscreenPublicMode. (b/35951989)
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardViewManager.isSecure(userId)) {
+ isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
+ }
+ }
+ setLockscreenPublicMode(isProfilePublic, userId);
+ }
+ }
+
/**
* Returns true if notifications are temporarily disabled for this user for security reasons,
* regardless of the normal settings for that user.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index d479838..f69ad43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -321,7 +321,7 @@
&& !row.isLowPriority()));
}
- entry.row.setShowAmbient(mPresenter.isDozing());
+ entry.row.setOnAmbient(mPresenter.isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.row.isRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 304a00f..2450e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.car;
import android.app.ActivityTaskManager;
-import android.car.user.CarUserManagerHelper;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.Log;
@@ -36,8 +35,8 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.car.hvac.TemperatureView;
@@ -461,16 +460,11 @@
}
}
-
- public boolean hasDockedTask() {
- return Recents.getSystemServices().hasDockedTask();
- }
-
/**
- * An implementation of SysUiTaskStackChangeListener, that listens for changes in the system
+ * An implementation of TaskStackChangeListener, that listens for changes in the system
* task stack and notifies the navigation bar.
*/
- private class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
+ private class TaskStackListenerImpl extends TaskStackChangeListener {
@Override
public void onTaskStackChanged() {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 67e512c..618a4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -22,7 +22,7 @@
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
-import android.car.user.CarUserManagerHelper;
+import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
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 53d38c4..f0e5462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -29,18 +29,14 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
/**
@@ -59,28 +55,28 @@
private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final StatusBarWindowView mStatusBarWindow;
- private final StatusBarStateController mStatusBarStateController;
- private StatusBar mStatusBar;
+ private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
- mStatusBar.collapsePanel(true /* animate */);
+ mCallback.onExpandAnimationTimedOut();
};
private boolean mAnimationPending;
+ private boolean mAnimationRunning;
+ private boolean mIsLaunchForActivity;
public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
- StatusBar statusBar,
+ Callback callback,
NotificationPanelView notificationPanel,
NotificationListContainer container) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
mStatusBarWindow = statusBarWindow;
- mStatusBar = statusBar;
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ mCallback = callback;
}
public RemoteAnimationAdapter getLaunchAnimation(
ExpandableNotificationRow sourceNotification, boolean occluded) {
- if (mStatusBarStateController.getState() != StatusBarState.SHADE || occluded) {
+ if (!mCallback.areLaunchAnimationsEnabled() || occluded) {
return null;
}
AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
@@ -92,10 +88,21 @@
return mAnimationPending;
}
- public void setLaunchResult(int launchResult) {
+ /**
+ * Set the launch result the intent requested
+ *
+ * @param launchResult the launch result
+ * @param wasIntentActivity was this launch for an activity
+ */
+ public void setLaunchResult(int launchResult, boolean wasIntentActivity) {
+ mIsLaunchForActivity = wasIntentActivity;
setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
|| launchResult == ActivityManager.START_SUCCESS)
- && mStatusBarStateController.getState() == StatusBarState.SHADE);
+ && mCallback.areLaunchAnimationsEnabled());
+ }
+
+ public boolean isLaunchForActivity() {
+ return mIsLaunchForActivity;
}
private void setAnimationPending(boolean pending) {
@@ -108,12 +115,16 @@
}
}
+ public boolean isAnimationRunning() {
+ return mAnimationRunning;
+ }
+
class AnimationRunner extends IRemoteAnimationRunner.Stub {
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
- private boolean mInstantCollapsePanel = true;
+ private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
@@ -136,10 +147,10 @@
}
setExpandAnimationRunning(true);
- mInstantCollapsePanel = primary.position.y == 0
+ mIsFullScreenLaunch = primary.position.y == 0
&& primary.sourceContainerBounds.height()
>= mNotificationPanel.getHeight();
- if (!mInstantCollapsePanel) {
+ if (!mIsFullScreenLaunch) {
mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
}
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
@@ -192,9 +203,6 @@
@Override
public void onAnimationEnd(Animator animation) {
setExpandAnimationRunning(false);
- if (mInstantCollapsePanel) {
- mStatusBar.collapsePanel(false /* animate */);
- }
invokeCallback(iRemoteAnimationFinishedCallback);
}
});
@@ -228,7 +236,9 @@
mSourceNotification.setExpandAnimationRunning(running);
mStatusBarWindow.setExpandAnimationRunning(running);
mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
+ mAnimationRunning = running;
if (!running) {
+ mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
applyParamsToNotification(null);
applyParamsToNotificationList(null);
}
@@ -257,7 +267,7 @@
public void onAnimationCancelled() throws RemoteException {
mSourceNotification.post(() -> {
setAnimationPending(false);
- mStatusBar.onLaunchAnimationCancelled();
+ mCallback.onLaunchAnimationCancelled();
});
}
};
@@ -319,4 +329,30 @@
return startTranslationZ;
}
}
+
+ public interface Callback {
+
+ /**
+ * Called when the launch animation was cancelled.
+ */
+ void onLaunchAnimationCancelled();
+
+ /**
+ * Called when the launch animation has timed out without starting an actual animation.
+ */
+ void onExpandAnimationTimedOut();
+
+ /**
+ * Called when the expand animation has finished.
+ *
+ * @param launchIsFullScreen True if this launch was fullscreen, such that now the window
+ * fills the whole screen
+ */
+ void onExpandAnimationFinished(boolean launchIsFullScreen);
+
+ /**
+ * Are animations currently enabled.
+ */
+ boolean areLaunchAnimationsEnabled();
+ }
}
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 804e842..d097c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -440,6 +440,8 @@
} else if (isHeadsUp) {
// Provide consistent ranking with headsUpManager
return mHeadsUpManager.compare(a, b);
+ } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
+ return a.row.isAmbientPulsing() ? -1 : 1;
} else if (aMedia != bMedia) {
// Upsort current media notification.
return aMedia ? -1 : 1;
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 ac01fa3..a3e982e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -28,15 +28,19 @@
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.os.Build;
+import android.os.Bundle;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
@@ -56,8 +60,9 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -91,7 +96,7 @@
ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
VisualStabilityManager.Callback {
private static final String TAG = "NotificationEntryMgr";
- protected static final boolean DEBUG = false;
+ 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";
@@ -121,12 +126,13 @@
Dependency.get(ForegroundServiceController.class);
protected final NotificationListener mNotificationListener =
Dependency.get(NotificationListener.class);
+ protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+ protected IDreamManager mDreamManager;
protected IStatusBarService mBarService;
protected NotificationPresenter mPresenter;
protected Callback mCallback;
protected PowerManager mPowerManager;
- protected SystemServicesProxy mSystemServicesProxy;
protected NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
@@ -218,8 +224,9 @@
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
mMessagingUtil = new NotificationMessagingUtil(context);
- mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
mGroupManager.setPendingEntries(mPendingNotifications);
}
@@ -264,6 +271,7 @@
}
mNotificationLifetimeExtenders.add(mHeadsUpManager);
+ mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders());
@@ -381,7 +389,7 @@
final int userId = n.getUserId();
try {
int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (isHeadsUp(n.getKey())) {
+ if (mHeadsUpManager.isAlerting(n.getKey())) {
dismissalSurface = NotificationStats.DISMISSAL_PEEK;
} else if (mListContainer.hasPulsingNotifications()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
@@ -432,12 +440,14 @@
}
private void addEntry(NotificationData.Entry shadeEntry) {
- boolean isHeadsUped = shouldPeek(shadeEntry);
- if (isHeadsUped) {
+ if (shouldHeadsUp(shadeEntry)) {
mHeadsUpManager.showNotification(shadeEntry);
// Mark as seen immediately
setNotificationShown(shadeEntry.notification);
}
+ if (shouldPulse(shadeEntry)) {
+ mAmbientPulseManager.showNotification(shadeEntry);
+ }
addNotificationViews(shadeEntry);
mCallback.onNotificationAdded(shadeEntry);
}
@@ -465,7 +475,11 @@
private void removeNotificationInternal(String key,
@Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
abortExistingInflation(key);
- if (mHeadsUpManager.contains(key)) {
+
+ // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
+ // Though the remove itself may fail, it lets the manager know to remove as soon as
+ // possible.
+ if (mHeadsUpManager.isAlerting(key)) {
// A cancel() in response to a remote input shouldn't be delayed, as it makes the
// sending look longer than it takes.
// Also we should not defer the removal if reordering isn't allowed since otherwise
@@ -473,10 +487,11 @@
boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
&& !FORCE_REMOTE_INPUT_HISTORY
|| !mVisualStabilityManager.isReorderingAllowed();
-
- // Attempt to remove notification.
mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
}
+ if (mAmbientPulseManager.isAlerting(key)) {
+ mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ }
NotificationData.Entry entry = mNotificationData.get(key);
@@ -651,13 +666,15 @@
private void addNotificationInternal(StatusBarNotification notification,
NotificationListenerService.RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
- if (DEBUG) Log.d(TAG, "addNotification key=" + key);
+ if (DEBUG) {
+ Log.d(TAG, "addNotification key=" + key);
+ }
mNotificationData.updateRanking(rankingMap);
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
- boolean isHeadsUped = shouldPeek(shadeEntry);
+ boolean isHeadsUped = shouldHeadsUp(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(shadeEntry)) {
if (DEBUG) {
@@ -672,7 +689,13 @@
} else {
// Stop screensaver if the notification has a fullscreen intent.
// (like an incoming phone call)
- SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
// not immersive & a fullscreen alert should be shown
if (DEBUG)
@@ -750,7 +773,6 @@
extender.setShouldManageLifetime(entry, false /* shouldManage */);
}
- Notification n = notification.getNotification();
mNotificationData.updateRanking(ranking);
final StatusBarNotification oldNotification = entry.notification;
@@ -763,10 +785,12 @@
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
- boolean shouldPeek = shouldPeek(entry, notification);
- boolean alertAgain = alertAgain(entry, n);
-
- updateHeadsUp(key, entry, shouldPeek, alertAgain);
+ boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
+ if (mPresenter.isDozing()) {
+ updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ } else {
+ updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ }
updateNotifications();
if (!notification.isClearable()) {
@@ -851,66 +875,163 @@
}
}
- protected boolean shouldPeek(NotificationData.Entry entry) {
- return shouldPeek(entry, entry.notification);
- }
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
- public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
- return false;
- }
-
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
- return false;
- }
-
- boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
-
- if (!inUse && !mPresenter.isDozing()) {
+ if (mPresenter.isDozing()) {
if (DEBUG) {
- Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
}
return false;
}
- if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
return false;
}
- // Peeking triggers an ambient display pulse, so disable peek is ambient is active
- if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
return false;
}
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ boolean isDreaming = false;
+ try {
+ isDreaming = mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query dream manager.", e);
+ }
+ boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.shouldSuppressPeek(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
return false;
}
if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
return false;
}
- // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
- int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
- : NotificationManager.IMPORTANCE_HIGH;
- if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
- if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
return false;
}
- // Don't peek notifications that are suppressed due to group alert behavior
+ if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mCallback.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ protected boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!mPresenter.isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.shouldSuppressAmbient(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.getImportance(sbn.getKey())
+ < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ Bundle extras = sbn.getNotification().extras;
+ CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+ CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+ if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
+ * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
+ * fail any of these checks should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationData.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
- return false;
- }
-
- if (!mCallback.shouldPeek(entry, sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
return false;
}
@@ -933,26 +1054,31 @@
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
- protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
- boolean alertAgain) {
- final boolean wasHeadsUp = isHeadsUp(key);
- if (wasHeadsUp) {
- if (!shouldPeek) {
+ /**
+ * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
+ * method.
+ * @param entry entry to update
+ * @param shouldAlert whether or not it should be alerting
+ * @param alertAgain whether or not an alert should actually come in as if it were new
+ * @param alertManager the alerting notification manager that manages the alert state
+ */
+ private void updateAlertState(NotificationData.Entry entry, boolean shouldAlert,
+ boolean alertAgain, AlertingNotificationManager alertManager) {
+ final boolean wasAlerting = alertManager.isAlerting(entry.key);
+ if (wasAlerting) {
+ if (!shouldAlert) {
// We don't want this to be interrupting anymore, lets remove it
- mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ alertManager.removeNotification(entry.key,
+ false /* ignoreEarliestRemovalTime */);
} else {
- mHeadsUpManager.updateNotification(entry.key, alertAgain);
+ alertManager.updateNotification(entry.key, alertAgain);
}
- } else if (shouldPeek && alertAgain) {
- // This notification was updated to be a heads-up, show it!
- mHeadsUpManager.showNotification(entry);
+ } else if (shouldAlert && alertAgain) {
+ // This notification was updated to be alerting, show it!
+ alertManager.showNotification(entry);
}
}
- protected boolean isHeadsUp(String key) {
- return mHeadsUpManager.contains(key);
- }
-
/**
* Callback for NotificationEntryManager.
*/
@@ -1008,12 +1134,12 @@
void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
/**
- * Returns true if NotificationEntryManager should peek this notification.
+ * Returns true if NotificationEntryManager can heads up this notification.
*
- * @param entry entry of the notification that might be peeked
- * @param sbn notification that might be peeked
- * @return true if the notification should be peeked
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return true if the notification can be heads upped
*/
- boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
}
}
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 216ed68..bce613a 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
@@ -72,7 +72,7 @@
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -174,6 +174,11 @@
*/
private boolean mOnKeyguard;
+ /**
+ * Whether or not the row is currently on the doze screen.
+ */
+ private boolean mOnAmbient;
+
private Animator mTranslateAnim;
private ArrayList<View> mTranslateableViews;
private NotificationContentView mPublicLayout;
@@ -186,7 +191,18 @@
private NotificationData.Entry mEntry;
private StatusBarNotification mStatusBarNotification;
private String mAppName;
+
+ /**
+ * Whether or not the notification is using the heads up view and should peek from the top.
+ */
private boolean mIsHeadsUp;
+
+ /**
+ * Whether or not the notification is using the ambient display view and is pulsing. This
+ * occurs when a high priority notification alerts while the phone is dozing or is on AOD.
+ */
+ private boolean mIsAmbientPulsing;
+
private boolean mLastChronometerRunning = true;
private ViewStub mChildrenContainerStub;
private NotificationGroupManager mGroupManager;
@@ -289,7 +305,6 @@
private float mContentTransformationAmount;
private boolean mIconsVisible = true;
private boolean mAboveShelf;
- private boolean mShowAmbient;
private boolean mIsLastChild;
private Runnable mOnDismissRunnable;
private boolean mIsLowPriority;
@@ -606,6 +621,14 @@
}
}
+ public boolean isAmbientPulsing() {
+ return mIsAmbientPulsing;
+ }
+
+ public void setAmbientPulsing(boolean isAmbientPulsing) {
+ mIsAmbientPulsing = isAmbientPulsing;
+ }
+
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
mPrivateLayout.setGroupManager(groupManager);
@@ -1014,7 +1037,7 @@
removeView(mMenuRow.getMenuView());
}
mMenuRow = plugin;
- if (mMenuRow.useDefaultMenuItems()) {
+ if (mMenuRow.shouldUseDefaultMenuItems()) {
ArrayList<MenuItem> items = new ArrayList<>();
items.add(NotificationMenuRow.createInfoItem(mContext));
items.add(NotificationMenuRow.createSnoozeItem(mContext));
@@ -1764,7 +1787,7 @@
getEntry().expandedIcon.setScrollX((int) -translationX);
}
if (mMenuRow.getMenuView() != null) {
- mMenuRow.onTranslationUpdate(translationX);
+ mMenuRow.onParentTranslationUpdate(translationX);
}
}
@@ -1854,7 +1877,7 @@
public void setDark(boolean dark, boolean fade, long delay) {
super.setDark(dark, fade, delay);
mDark = dark;
- if (!mIsHeadsUp) {
+ if (!mIsAmbientPulsing) {
// Only fade the showing view of the pulsing notification.
fade = false;
}
@@ -2155,7 +2178,7 @@
return mPrivateLayout.getMinHeight();
} else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
return getMinHeight();
- } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
+ } else if (mIsSummaryWithChildren && (!mOnKeyguard || mOnAmbient)) {
return mChildrenContainer.getIntrinsicHeight();
} else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
if (isPinned() || mHeadsupDisappearRunning) {
@@ -2173,7 +2196,7 @@
}
private boolean isHeadsUpAllowed() {
- return !mOnKeyguard && !mShowAmbient;
+ return !mOnKeyguard && !mOnAmbient;
}
@Override
@@ -2269,7 +2292,7 @@
notifyHeightChanged(true /* needsAnimation */);
}
if (mMenuRow.getMenuView() != null) {
- mMenuRow.onHeightUpdate();
+ mMenuRow.onParentHeightUpdate();
}
updateContentShiftHeight();
if (mLayoutListener != null) {
@@ -2520,7 +2543,7 @@
mGuts.setActualHeight(height);
}
if (mMenuRow.getMenuView() != null) {
- mMenuRow.onHeightUpdate();
+ mMenuRow.onParentHeightUpdate();
}
}
@@ -2818,11 +2841,11 @@
|| mExpandAnimationRunning || mChildIsExpanding);
}
- public void setShowAmbient(boolean showAmbient) {
- if (showAmbient != mShowAmbient) {
- mShowAmbient = showAmbient;
+ public void setOnAmbient(boolean onAmbient) {
+ if (onAmbient != mOnAmbient) {
+ mOnAmbient = onAmbient;
if (mChildrenContainer != null) {
- mChildrenContainer.notifyShowAmbientChanged();
+ mChildrenContainer.notifyDozingStateChanged();
}
notifyHeightChanged(false /* needsAnimation */);
}
@@ -2891,8 +2914,8 @@
return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
}
- public boolean isShowingAmbient() {
- return mShowAmbient;
+ public boolean isOnAmbient() {
+ return mOnAmbient;
}
public void setAboveShelf(boolean aboveShelf) {
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 0110610..4963a0c 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
@@ -725,7 +725,7 @@
}
public int getMaxHeight() {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return getShowingAmbientView().getHeight();
} else if (mExpandedChild != null) {
return getViewHeight(VISIBLE_TYPE_EXPANDED)
@@ -752,7 +752,7 @@
}
public int getMinHeight(boolean likeGroupExpanded) {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return getShowingAmbientView().getHeight();
} else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
return getViewHeight(VISIBLE_TYPE_CONTRACTED);
@@ -1039,7 +1039,7 @@
* @return one of the static enum types in this view, calculated form the current state
*/
public int calculateVisibleType() {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
if (mIsChildInGroup && mAmbientSingleLineChild != null) {
return VISIBLE_TYPE_AMBIENT_SINGLELINE;
} else if (mAmbientChild != null) {
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 9e2331f..903c272 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
@@ -103,8 +103,15 @@
};
private OnClickListener mOnStopOrMinimizeNotifications = v -> {
- mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
- swapContent(false);
+ Runnable saveImportance = () -> {
+ mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
+ swapContent(false);
+ };
+ if (mCheckSaveListener != null) {
+ mCheckSaveListener.checkSave(saveImportance, mSbn);
+ } else {
+ saveImportance.run();
+ }
};
private OnClickListener mOnUndo = v -> {
@@ -304,15 +311,7 @@
private void saveImportance() {
if (!mIsNonblockable) {
- // Only go through the lock screen/bouncer if the user hit 'Stop notifications'.
- // Otherwise, update the importance immediately.
- if (mCheckSaveListener != null
- && NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS.equals(
- mExitReason)) {
- mCheckSaveListener.checkSave(this::updateImportance, mSbn);
- } else {
- updateImportance();
- }
+ updateImportance();
}
}
@@ -523,6 +522,11 @@
return getHeight();
}
+ @VisibleForTesting
+ public boolean isAnimating() {
+ return mExpandAnimation != null && mExpandAnimation.isRunning();
+ }
+
/**
* Runnable to either update the given channel (with a new importance value) or, if no channel
* is provided, update notifications enabled state for the package.
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 dec88d4..7e60c4b 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
@@ -16,14 +16,13 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-
import java.util.ArrayList;
+import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -38,25 +37,21 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
+import com.android.internal.annotations.VisibleForTesting;
+
public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
ExpandableNotificationRow.LayoutListener {
private static final boolean DEBUG = false;
private static final String TAG = "swipe";
- private static final int ICON_ALPHA_ANIM_DURATION = 200;
- private static final long SHOW_MENU_DELAY = 60;
- private static final long SWIPE_MENU_TIMING = 200;
-
// Notification must be swiped at least this fraction of a single menu item to show menu
private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
@@ -65,6 +60,9 @@
// menu item to snap back to menu (else it will cover the menu or it'll be dismissed)
private static final float SWIPED_BACK_ENOUGH_TO_COVER_FRACTION = 0.2f;
+ private static final int ICON_ALPHA_ANIM_DURATION = 200;
+ private static final long SHOW_MENU_DELAY = 60;
+
private ExpandableNotificationRow mParent;
private Context mContext;
@@ -89,22 +87,20 @@
private int[] mIconLocation = new int[2];
private int[] mParentLocation = new int[2];
- private float mHorizSpaceForIcon = -1;
+ private int mHorizSpaceForIcon = -1;
private int mVertSpaceForIcons = -1;
private int mIconPadding = -1;
private int mSidePadding;
private float mAlpha = 0f;
- private float mPrevX;
private CheckForDrag mCheckForDrag;
private Handler mHandler;
- private boolean mMenuSnappedTo;
+ private boolean mMenuSnapped;
private boolean mMenuSnappedOnLeft;
private boolean mShouldShowMenu;
- private NotificationSwipeActionHelper mSwipeHelper;
private boolean mIsUserTouching;
public NotificationMenuRow(Context context) {
@@ -134,9 +130,34 @@
return mSnoozeItem;
}
- @Override
- public void setSwipeActionHelper(NotificationSwipeActionHelper helper) {
- mSwipeHelper = helper;
+ @VisibleForTesting
+ protected ExpandableNotificationRow getParent() {
+ return mParent;
+ }
+
+ @VisibleForTesting
+ protected boolean isMenuOnLeft() {
+ return mOnLeft;
+ }
+
+ @VisibleForTesting
+ protected boolean isMenuSnappedOnLeft() {
+ return mMenuSnappedOnLeft;
+ }
+
+ @VisibleForTesting
+ protected boolean isMenuSnapped() {
+ return mMenuSnapped;
+ }
+
+ @VisibleForTesting
+ protected boolean isDismissing() {
+ return mDismissing;
+ }
+
+ @VisibleForTesting
+ protected boolean isSnapping() {
+ return mSnapping;
}
@Override
@@ -155,17 +176,37 @@
return mAlpha > 0;
}
+ @VisibleForTesting
+ protected boolean isUserTouching() {
+ return mIsUserTouching;
+ }
+
+ @Override
+ public boolean shouldShowMenu() {
+ return mShouldShowMenu;
+ }
+
@Override
public View getMenuView() {
return mMenuContainer;
}
+ @VisibleForTesting
+ protected float getTranslation() {
+ return mTranslation;
+ }
+
@Override
public void resetMenu() {
resetState(true);
}
@Override
+ public void onTouchEnd() {
+ mIsUserTouching = false;
+ }
+
+ @Override
public void onNotificationUpdated(StatusBarNotification sbn) {
if (mMenuContainer == null) {
// Menu hasn't been created yet, no need to do anything.
@@ -222,9 +263,7 @@
mIconsPlaced = false;
setMenuLocation();
if (!mIsUserTouching) {
- // If the # of items showing changed we need to update the snap position
- showMenu(mParent, mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(),
- 0 /* velocity */);
+ onSnapOpen();
}
}
}
@@ -236,7 +275,7 @@
mAnimating = false;
mSnapping = false;
mDismissing = false;
- mMenuSnappedTo = false;
+ mMenuSnapped = false;
setMenuLocation();
if (mMenuListener != null && notify) {
mMenuListener.onMenuReset(mParent);
@@ -244,185 +283,102 @@
}
@Override
- public boolean onTouchEvent(View view, MotionEvent ev, float velocity) {
- final int action = ev.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mSnapping = false;
- if (mFadeAnimator != null) {
- mFadeAnimator.cancel();
- }
- mHandler.removeCallbacks(mCheckForDrag);
+ public void onTouchMove(float delta) {
+ mSnapping = false;
+
+ if (!isTowardsMenu(delta) && isMenuLocationChange()) {
+ // Don't consider it "snapped" if location has changed.
+ mMenuSnapped = false;
+
+ // Changed directions, make sure we check to fade in icon again.
+ if (!mHandler.hasCallbacks(mCheckForDrag)) {
+ // No check scheduled, set null to schedule a new one.
mCheckForDrag = null;
- mPrevX = ev.getRawX();
- mIsUserTouching = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- mSnapping = false;
- float diffX = ev.getRawX() - mPrevX;
- mPrevX = ev.getRawX();
- if (!isTowardsMenu(diffX) && isMenuLocationChange()) {
- // Don't consider it "snapped" if location has changed.
- mMenuSnappedTo = false;
-
- // Changed directions, make sure we check to fade in icon again.
- if (!mHandler.hasCallbacks(mCheckForDrag)) {
- // No check scheduled, set null to schedule a new one.
- mCheckForDrag = null;
- } else {
- // Check scheduled, reset alpha and update location; check will fade it in
- setMenuAlpha(0f);
- setMenuLocation();
- }
- }
- if (mShouldShowMenu
- && !NotificationStackScrollLayout.isPinnedHeadsUp(view)
- && !mParent.areGutsExposed()
- && !mParent.isDark()
- && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
- // Only show the menu if we're not a heads up view and guts aren't exposed.
- mCheckForDrag = new CheckForDrag();
- mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- mIsUserTouching = false;
- return handleUpEvent(ev, view, velocity);
- case MotionEvent.ACTION_CANCEL:
- mIsUserTouching = false;
- cancelDrag();
- return false;
- }
- return false;
- }
-
- private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) {
- // If the menu should not be shown, then there is no need to check if the a swipe
- // should result in a snapping to the menu. As a result, just check if the swipe
- // was enough to dismiss the notification.
- if (!mShouldShowMenu) {
- if (mSwipeHelper.isDismissGesture(ev)) {
- dismiss(animView, velocity);
} else {
- snapBack(animView, velocity);
+ // Check scheduled, reset alpha and update location; check will fade it in
+ setMenuAlpha(0f);
+ setMenuLocation();
}
- return true;
}
-
- final boolean gestureTowardsMenu = isTowardsMenu(velocity);
- final boolean gestureFastEnough =
- mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
- final boolean gestureFarEnough =
- mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth());
- final double timeForGesture = ev.getEventTime() - ev.getDownTime();
- final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed()
- && timeForGesture >= SWIPE_MENU_TIMING;
- final float menuSnapTarget = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu();
-
- if (DEBUG) {
- Log.d(TAG, "mTranslation= " + mTranslation
- + " mAlpha= " + mAlpha
- + " velocity= " + velocity
- + " mMenuSnappedTo= " + mMenuSnappedTo
- + " mMenuSnappedOnLeft= " + mMenuSnappedOnLeft
- + " mOnLeft= " + mOnLeft
- + " minDismissVel= " + mSwipeHelper.getMinDismissVelocity()
- + " isDismissGesture= " + mSwipeHelper.isDismissGesture(ev)
- + " gestureTowardsMenu= " + gestureTowardsMenu
- + " gestureFastEnough= " + gestureFastEnough
- + " gestureFarEnough= " + gestureFarEnough);
+ if (mShouldShowMenu
+ && !NotificationStackScrollLayout.isPinnedHeadsUp(getParent())
+ && !mParent.areGutsExposed()
+ && !mParent.isDark()
+ && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
+ // Only show the menu if we're not a heads up view and guts aren't exposed.
+ mCheckForDrag = new CheckForDrag();
+ mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
}
-
- if (mMenuSnappedTo && isMenuVisible() && mMenuSnappedOnLeft == mOnLeft) {
- // Menu was snapped to previously and we're on the same side, figure out if
- // we should stick to the menu, snap back into place, or dismiss
- final float maximumSwipeDistance = mHorizSpaceForIcon
- * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
- final float targetLeft = getSpaceForMenu() - maximumSwipeDistance;
- final float targetRight = mParent.getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
- boolean withinSnapMenuThreshold = mOnLeft
- ? mTranslation > targetLeft && mTranslation < targetRight
- : mTranslation < -targetLeft && mTranslation > -targetRight;
- boolean shouldSnapTo = mOnLeft ? mTranslation < targetLeft : mTranslation > -targetLeft;
- if (DEBUG) {
- Log.d(TAG, " withinSnapMenuThreshold= " + withinSnapMenuThreshold
- + " shouldSnapTo= " + shouldSnapTo
- + " targetLeft= " + targetLeft
- + " targetRight= " + targetRight);
- }
- if (withinSnapMenuThreshold && !mSwipeHelper.isDismissGesture(ev)) {
- // Haven't moved enough to unsnap from the menu
- showMenu(animView, menuSnapTarget, velocity);
- } else if (mSwipeHelper.isDismissGesture(ev) && !shouldSnapTo) {
- // Only dismiss if we're not moving towards the menu
- dismiss(animView, velocity);
- } else {
- snapBack(animView, velocity);
- }
- } else if (!mSwipeHelper.isFalseGesture(ev)
- && (swipedEnoughToShowMenu() && (!gestureFastEnough || showMenuForSlowOnGoing))
- || (gestureTowardsMenu && !mSwipeHelper.isDismissGesture(ev))) {
- // Menu has not been snapped to previously and this is menu revealing gesture
- showMenu(animView, menuSnapTarget, velocity);
- } else if (mSwipeHelper.isDismissGesture(ev) && !gestureTowardsMenu) {
- dismiss(animView, velocity);
- } else {
- snapBack(animView, velocity);
- }
- return true;
}
- private void showMenu(View animView, float targetLeft, float velocity) {
- mMenuSnappedTo = true;
- mMenuSnappedOnLeft = mOnLeft;
- mMenuListener.onMenuShown(animView);
- mSwipeHelper.snap(animView, targetLeft, velocity);
+ @VisibleForTesting
+ protected void beginDrag() {
+ mSnapping = false;
+ if (mFadeAnimator != null) {
+ mFadeAnimator.cancel();
+ }
+ mHandler.removeCallbacks(mCheckForDrag);
+ mCheckForDrag = null;
+ mIsUserTouching = true;
}
- private void snapBack(View animView, float velocity) {
+ @Override
+ public void onTouchStart() {
+ beginDrag();
+ }
+
+ @Override
+ public void onSnapOpen() {
+ mMenuSnapped = true;
+ mMenuSnappedOnLeft = isMenuOnLeft();
+ if (mMenuListener != null) {
+ mMenuListener.onMenuShown(getParent());
+ }
+ }
+
+ @Override
+ public void onSnapClosed() {
cancelDrag();
- mMenuSnappedTo = false;
+ mMenuSnapped = false;
mSnapping = true;
- mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity);
}
- private void dismiss(View animView, float velocity) {
+ @Override
+ public void onDismiss() {
cancelDrag();
- mMenuSnappedTo = false;
+ mMenuSnapped = false;
mDismissing = true;
- mSwipeHelper.dismiss(animView, velocity);
}
- private void cancelDrag() {
+ @VisibleForTesting
+ protected void cancelDrag() {
if (mFadeAnimator != null) {
mFadeAnimator.cancel();
}
mHandler.removeCallbacks(mCheckForDrag);
}
- /**
- * @return whether the notification has been translated enough to show the menu and not enough
- * to be dismissed.
- */
- private boolean swipedEnoughToShowMenu() {
- final float multiplier = mParent.canViewBeDismissed()
+ @VisibleForTesting
+ protected float getMinimumSwipeDistance() {
+ final float multiplier = getParent().canViewBeDismissed()
? SWIPED_FAR_ENOUGH_MENU_FRACTION
: SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION;
- final float minimumSwipeDistance = mHorizSpaceForIcon * multiplier;
- return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible()
- && (mOnLeft ? mTranslation > minimumSwipeDistance
- : mTranslation < -minimumSwipeDistance);
+ return mHorizSpaceForIcon * multiplier;
+ }
+
+ @VisibleForTesting
+ protected float getMaximumSwipeDistance() {
+ return mHorizSpaceForIcon * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
}
/**
* Returns whether the gesture is towards the menu location or not.
*/
- private boolean isTowardsMenu(float movement) {
+ @Override
+ public boolean isTowardsMenu(float movement) {
return isMenuVisible()
- && ((mOnLeft && movement <= 0)
- || (!mOnLeft && movement >= 0));
+ && ((isMenuOnLeft() && movement <= 0)
+ || (!isMenuOnLeft() && movement >= 0));
}
@Override
@@ -445,7 +401,7 @@
}
@Override
- public void onHeightUpdate() {
+ public void onParentHeightUpdate() {
if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
return;
}
@@ -460,7 +416,7 @@
}
@Override
- public void onTranslationUpdate(float translation) {
+ public void onParentTranslationUpdate(float translation) {
mTranslation = translation;
if (mAnimating || !mMenuFadedIn) {
// Don't adjust when animating, or if the menu hasn't been shown yet.
@@ -492,13 +448,15 @@
final int x = mIconLocation[0] - mParentLocation[0] + centerX;
final int y = mIconLocation[1] - mParentLocation[1] + centerY;
final int index = mMenuContainer.indexOfChild(v);
- mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+ if (mMenuListener != null) {
+ mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+ }
}
private boolean isMenuLocationChange() {
boolean onLeft = mTranslation > mIconPadding;
boolean onRight = mTranslation < -mIconPadding;
- if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+ if ((isMenuOnLeft() && onRight) || (!isMenuOnLeft() && onLeft)) {
return true;
}
return false;
@@ -506,7 +464,7 @@
private void setMenuLocation() {
boolean showOnLeft = mTranslation > 0;
- if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mMenuContainer == null
+ if ((mIconsPlaced && showOnLeft == isMenuOnLeft()) || isSnapping() || mMenuContainer == null
|| !mMenuContainer.isAttachedToWindow()) {
// Do nothing
return;
@@ -522,7 +480,8 @@
mIconsPlaced = true;
}
- private void setMenuAlpha(float alpha) {
+ @VisibleForTesting
+ protected void setMenuAlpha(float alpha) {
mAlpha = alpha;
if (mMenuContainer == null) {
return;
@@ -542,7 +501,8 @@
/**
* Returns the horizontal space in pixels required to display the menu.
*/
- private float getSpaceForMenu() {
+ @VisibleForTesting
+ protected int getSpaceForMenu() {
return mHorizSpaceForIcon * mMenuContainer.getChildCount();
}
@@ -646,12 +606,71 @@
parent.addView(menuView);
menuView.setOnClickListener(this);
FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
- lp.width = (int) mHorizSpaceForIcon;
- lp.height = (int) mHorizSpaceForIcon;
+ lp.width = mHorizSpaceForIcon;
+ lp.height = mHorizSpaceForIcon;
menuView.setLayoutParams(lp);
}
}
+ @VisibleForTesting
+ /**
+ * Determine the minimum offset below which the menu should snap back closed.
+ */
+ protected float getSnapBackThreshold() {
+ return getSpaceForMenu() - getMaximumSwipeDistance();
+ }
+
+ /**
+ * Determine the maximum offset above which the parent notification should be dismissed.
+ * @return
+ */
+ @VisibleForTesting
+ protected float getDismissThreshold() {
+ return getParent().getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+ }
+
+ @Override
+ public boolean isWithinSnapMenuThreshold() {
+ float translation = getTranslation();
+ float snapBackThreshold = getSnapBackThreshold();
+ float targetRight = getDismissThreshold();
+ return isMenuOnLeft()
+ ? translation > snapBackThreshold && translation < targetRight
+ : translation < -snapBackThreshold && translation > -targetRight;
+ }
+
+ @Override
+ public boolean isSwipedEnoughToShowMenu() {
+ final float minimumSwipeDistance = getMinimumSwipeDistance();
+ final float translation = getTranslation();
+ return isMenuVisible() && (isMenuOnLeft() ?
+ translation > minimumSwipeDistance
+ : translation < -minimumSwipeDistance);
+ }
+
+ @Override
+ public int getMenuSnapTarget() {
+ return isMenuOnLeft() ? getSpaceForMenu() : -getSpaceForMenu();
+ }
+
+ @Override
+ public boolean shouldSnapBack() {
+ float translation = getTranslation();
+ float targetLeft = getSnapBackThreshold();
+ return isMenuOnLeft() ? translation < targetLeft : translation > -targetLeft;
+ }
+
+ @Override
+ public boolean isSnappedAndOnSameSide() {
+ return isMenuSnapped() && isMenuVisible()
+ && isMenuSnappedOnLeft() == isMenuOnLeft();
+ }
+
+ @Override
+ public boolean canBeDismissed() {
+ return getParent().canViewBeDismissed();
+ }
+
public static class NotificationMenuItem implements MenuItem {
View mMenuView;
GutsContent mGutsContent;
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 15eaaac..8969aca 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
@@ -20,14 +20,15 @@
import android.content.Context;
import android.view.View;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
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.notification.NotificationData;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
@@ -44,7 +45,7 @@
private int mSpeedBumpIndex = -1;
private boolean mDark;
private boolean mHideSensitive;
- private HeadsUpManager mHeadsUpManager;
+ private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private float mStackTranslation;
private int mLayoutHeight;
private int mTopPadding;
@@ -207,10 +208,6 @@
mSpeedBumpIndex = shelfIndex;
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public float getStackTranslation() {
return mStackTranslation;
}
@@ -334,10 +331,10 @@
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (!mPulsing || mHeadsUpManager == null) {
+ if (!mPulsing || mAmbientPulseManager == null) {
return false;
}
- return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+ return mAmbientPulseManager.isAlerting(entry.key);
}
public boolean isPanelTracking() {
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 3d44e3c..da089b3 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
@@ -213,7 +213,7 @@
// calculated correctly as they are used to calculate how many we can fit on the screen.
boolean isOverflow = i == overflowIndex;
child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
- !mContainingNotification.isShowingAmbient()
+ !mContainingNotification.isOnAmbient()
? mOverflowNumber.getMeasuredWidth() : 0);
child.measure(widthMeasureSpec, newHeightSpec);
// layout the divider
@@ -406,7 +406,7 @@
if (childCount > maxAllowedVisibleChildren) {
int number = childCount - maxAllowedVisibleChildren;
mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
ExpandableNotificationRow overflowView = mChildren.get(0);
HybridNotificationView ambientSingleLineView = overflowView == null ? null
: overflowView.getAmbientSingleLineView();
@@ -522,7 +522,7 @@
if (mUserLocked) {
expandFactor = getGroupExpandFraction();
}
- boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
+ boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isOnAmbient();
for (int i = 0; i < childCount; i++) {
if (visibleChildren >= maxAllowedVisibleChildren) {
break;
@@ -641,7 +641,7 @@
getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
mGroupOverFlowState.alpha = 0.0f;
} else if (!mChildrenExpanded) {
HybridNotificationView alignView = overflowView.getSingleLineView();
@@ -710,7 +710,7 @@
@VisibleForTesting
int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
}
if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
@@ -900,7 +900,7 @@
return mCurrentHeader;
}
- public void notifyShowAmbientChanged() {
+ public void notifyDozingStateChanged() {
updateHeaderVisibility(false);
updateGroupOverflow();
}
@@ -970,7 +970,7 @@
private ViewGroup calculateDesiredHeader() {
ViewGroup desiredHeader;
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
desiredHeader = mNotificationHeaderAmbient;
} else if (showingAsLowPriority()) {
desiredHeader = mNotificationHeaderLowPriority;
@@ -1126,7 +1126,7 @@
}
public int getMinHeight() {
- return getMinHeight(mContainingNotification.isShowingAmbient()
+ return getMinHeight(mContainingNotification.isOnAmbient()
? NUMBER_OF_CHILDREN_WHEN_AMBIENT
: NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
}
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 da98565..9978ec3 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
@@ -26,7 +26,6 @@
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.app.WallpaperManager;
import android.content.Context;
@@ -88,7 +87,6 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
@@ -110,6 +108,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarStateController;
@@ -147,11 +146,9 @@
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
public class NotificationStackScrollLayout extends ViewGroup
- implements Callback, ExpandHelper.Callback, ScrollAdapter,
- OnHeightChangedListener, OnGroupChangeListener,
- OnMenuEventListener, VisibilityLocationProvider,
- NotificationListContainer, ConfigurationListener, DragDownCallback, AnimationStateHandler,
- Dumpable {
+ implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener,
+ OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer,
+ ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -165,7 +162,7 @@
private static final int INVALID_POINTER = -1;
private ExpandHelper mExpandHelper;
- private NotificationSwipeHelper mSwipeHelper;
+ private final NotificationSwipeHelper mSwipeHelper;
private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
private final Paint mBackgroundPaint = new Paint();
@@ -292,10 +289,6 @@
*/
private int mMaxScrollAfterExpand;
private ExpandableNotificationRow.LongPressListener mLongPressListener;
-
- private NotificationMenuRowPlugin mCurrMenuRow;
- private View mTranslatingParentView;
- private View mMenuExposedView;
boolean mCheckForLeavebehind;
/**
@@ -467,6 +460,9 @@
private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
private NotificationPanelView mNotificationPanel;
+ private final NotificationGutsManager
+ mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -496,7 +492,8 @@
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
- mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
+ mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(),
+ getContext(), new NotificationMenuListener());
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
@@ -640,41 +637,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onMenuClicked(View view, int x, int y, MenuItem item) {
- if (mLongPressListener == null) {
- return;
- }
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
- row.getStatusBarNotification().getPackageName());
- }
- mLongPressListener.onLongPress(view, x, y, item);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onMenuReset(View row) {
- if (mTranslatingParentView != null && row == mTranslatingParentView) {
- mMenuExposedView = null;
- mTranslatingParentView = null;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onMenuShown(View row) {
- mMenuExposedView = mTranslatingParentView;
- if (row instanceof ExpandableNotificationRow) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
- ((ExpandableNotificationRow) row).getStatusBarNotification()
- .getPackageName());
- }
- mSwipeHelper.onMenuShown(row);
- }
-
- @Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onUiModeChanged() {
mBgColor = mContext.getColor(R.color.notification_shade_background_color);
@@ -1296,111 +1258,6 @@
mQsContainer = qsContainer;
}
- /**
- * Handles cleanup after the given {@code view} has been fully swiped out (including
- * re-invoking dismiss logic in case the notification has not made its way out yet).
- */
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onChildDismissed(View view) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (!row.isDismissed()) {
- handleChildViewDismissed(view);
- }
- ViewGroup transientContainer = row.getTransientContainer();
- if (transientContainer != null) {
- transientContainer.removeTransientView(view);
- }
- }
-
- /**
- * Starts up notification dismiss and tells the notification, if any, to remove itself from
- * layout.
- *
- * @param view view (e.g. notification) to dismiss from the layout
- */
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void handleChildViewDismissed(View view) {
- if (mDismissAllInProgress) {
- return;
- }
-
- boolean isBlockingHelperShown = false;
-
- setSwipingInProgress(false);
- if (mDragAnimPendingChildren.contains(view)) {
- // We start the swipe and finish it in the same frame; we don't want a drag animation.
- mDragAnimPendingChildren.remove(view);
- }
- mAmbientState.onDragFinished(view);
- updateContinuousShadowDrawing();
-
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (row.isHeadsUp()) {
- mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
- }
- isBlockingHelperShown =
- row.performDismissWithBlockingHelper(false /* fromAccessibility */);
- }
-
- if (!isBlockingHelperShown) {
- mSwipedOutViews.add(view);
- }
- mFalsingManager.onNotificationDismissed();
- if (mFalsingManager.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
- null,
- null /* cancelAction */,
- false /* dismissShade */,
- true /* afterKeyguardGone */,
- false /* deferred */);
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onChildSnappedBack(View animView, float targetLeft) {
- mAmbientState.onDragFinished(animView);
- updateContinuousShadowDrawing();
- if (!mDragAnimPendingChildren.contains(animView)) {
- if (mAnimationsEnabled) {
- mSnappedBackChildren.add(animView);
- mNeedsAnimation = true;
- }
- requestChildrenUpdate();
- } else {
- // We start the swipe and snap back in the same frame, we don't want any animation
- mDragAnimPendingChildren.remove(animView);
- }
- if (mCurrMenuRow != null && targetLeft == 0) {
- mCurrMenuRow.resetMenu();
- mCurrMenuRow = null;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
- // Returning true prevents alpha fading.
- return !mFadeNotificationsOnDismiss;
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onBeginDrag(View v) {
- mFalsingManager.onNotificatonStartDismissing();
- setSwipingInProgress(true);
- mAmbientState.onBeginDrag(v);
- updateContinuousShadowDrawing();
- if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
- mDragAnimPendingChildren.add(v);
- mNeedsAnimation = true;
- }
- requestChildrenUpdate();
- }
-
@ShadeViewRefactor(RefactorComponent.ADAPTER)
public static boolean isPinnedHeadsUp(View v) {
if (v instanceof ExpandableNotificationRow) {
@@ -1419,41 +1276,6 @@
return false;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onDragCancelled(View v) {
- mFalsingManager.onNotificatonStopDismissing();
- setSwipingInProgress(false);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public float getFalsingThresholdFactor() {
- return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public View getChildAtPosition(MotionEvent ev) {
- View child = getChildAtPosition(ev.getX(), ev.getY());
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- ExpandableNotificationRow parent = row.getNotificationParent();
- if (parent != null && parent.areChildrenExpanded()
- && (parent.areGutsExposed()
- || mMenuExposedView == parent
- || (parent.getNotificationChildren().size() == 1
- && parent.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
- // if we only have a single child.
- child = parent;
- }
- }
- return child;
- }
-
@ShadeViewRefactor(RefactorComponent.INPUT)
public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
@@ -1697,18 +1519,11 @@
return mScrollingEnabled;
}
- @Override
@ShadeViewRefactor(RefactorComponent.ADAPTER)
- public boolean canChildBeDismissed(View v) {
+ private boolean canChildBeDismissed(View v) {
return StackScrollAlgorithm.canChildBeDismissed(v);
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean isAntiFalsingNeeded() {
- return onKeyguard();
- }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean onKeyguard() {
return mStatusBarState == StatusBarState.KEYGUARD;
@@ -1788,8 +1603,8 @@
}
// Check if we need to clear any snooze leavebehinds
- NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
- if (guts != null && !isTouchInView(ev, guts)
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
&& guts.getGutsContent() instanceof NotificationSnooze) {
NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
if ((ns.isExpanded() && isCancelOrUp)
@@ -3014,11 +2829,11 @@
}
// Check if we need to clear any snooze leavebehinds
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
- if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
- && !scrollWantsIt) {
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+ !expandWantsIt && !scrollWantsIt) {
mCheckForLeavebehind = false;
- mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
false /* resetMenu */);
}
@@ -3078,8 +2893,8 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@Override
public void cleanUpViewState(View child) {
- if (child == mTranslatingParentView) {
- mTranslatingParentView = null;
+ if (child == mSwipeHelper.getTranslatingParentView()) {
+ mSwipeHelper.clearTranslatingParentView();
}
mCurrentStackScrollState.removeViewStateForView(child);
}
@@ -3987,7 +3802,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void checkSnoozeLeavebehind() {
if (mCheckForLeavebehind) {
- mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
false /* resetMenu */);
mCheckForLeavebehind = false;
@@ -4069,7 +3884,7 @@
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void setIsExpanded(boolean isExpanded) {
+ public void setIsExpanded(boolean isExpanded) {
boolean changed = isExpanded != mIsExpanded;
mIsExpanded = isExpanded;
mStackScrollAlgorithm.setIsExpanded(isExpanded);
@@ -4828,7 +4643,6 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
- mAmbientState.setHeadsUpManager(headsUpManager);
mHeadsUpManager.addListener(mRoundnessManager);
mHeadsUpManager.setAnimationStateHandler(this);
}
@@ -5244,8 +5058,8 @@
setFooterView(footerView);
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void inflateEmptyShadeView() {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private void inflateEmptyShadeView() {
EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, this, false);
view.setText(R.string.empty_shade_text);
@@ -5276,8 +5090,8 @@
mScrimController.setNotificationCount(getNotGoneChildCount());
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setNotificationPanel(NotificationPanelView notificationPanelView) {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void setNotificationPanel(NotificationPanelView notificationPanelView) {
mNotificationPanel = notificationPanelView;
}
@@ -5295,226 +5109,29 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public interface OnOverscrollTopChangedListener {
- /**
- * Notifies a listener that the overscroll has changed.
- *
- * @param amount the amount of overscroll, in pixels
- * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
- * unrubberbanded motion to directly expand overscroll view (e.g
- * expand
- * QS)
- */
- void onOverscrollTopChanged(float amount, boolean isRubberbanded);
+ /**
+ * Notifies a listener that the overscroll has changed.
+ *
+ * @param amount the amount of overscroll, in pixels
+ * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+ * unrubberbanded motion to directly expand overscroll view (e.g
+ * expand
+ * QS)
+ */
+ void onOverscrollTopChanged(float amount, boolean isRubberbanded);
- /**
- * Notify a listener that the scroller wants to escape from the scrolling motion and
- * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
- *
- * @param velocity The velocity that the Scroller had when over flinging
- * @param open Should the fling open or close the overscroll view.
- */
- void flingTopOverscroll(float velocity, boolean open);
- }
+ /**
+ * Notify a listener that the scroller wants to escape from the scrolling motion and
+ * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
+ *
+ * @param velocity The velocity that the Scroller had when over flinging
+ * @param open Should the fling open or close the overscroll view.
+ */
+ void flingTopOverscroll(float velocity, boolean open);
+ }
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private class NotificationSwipeHelper extends SwipeHelper
- implements NotificationSwipeActionHelper {
- private static final long COVER_MENU_DELAY = 4000;
- private Runnable mFalsingCheck;
- private Handler mHandler;
-
- public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
- super(swipeDirection, callback, context);
- mHandler = new Handler();
- mFalsingCheck = new Runnable() {
- @Override
- public void run() {
- resetExposedMenuView(true /* animate */, true /* force */);
- }
- };
- }
-
- @Override
- public void onDownUpdate(View currView, MotionEvent ev) {
- mTranslatingParentView = currView;
- if (mCurrMenuRow != null) {
- mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
- }
- mCurrMenuRow = null;
- mHandler.removeCallbacks(mFalsingCheck);
-
- // Slide back any notifications that might be showing a menu
- resetExposedMenuView(true /* animate */, false /* force */);
-
- if (currView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
-
- if (row.getEntry().hasFinishedInitialization()) {
- mCurrMenuRow = row.createMenu();
- mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
- mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
- mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
- }
- }
- }
-
- @Override
- public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
- mHandler.removeCallbacks(mFalsingCheck);
- if (mCurrMenuRow != null) {
- mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
- }
- }
-
- @Override
- public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
- float translation) {
- if (mCurrMenuRow != null) {
- return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
- }
- return false;
- }
-
- @Override
- public void dismissChild(final View view, float velocity,
- boolean useAccelerateInterpolator) {
- super.dismissChild(view, velocity, useAccelerateInterpolator);
- if (mIsExpanded) {
- // We don't want to quick-dismiss when it's a heads up as this might lead to closing
- // of the panel early.
- handleChildViewDismissed(view);
- }
- mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
- false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- handleMenuCoveredOrDismissed();
- }
-
- @Override
- public void snapChild(final View animView, final float targetLeft, float velocity) {
- super.snapChild(animView, targetLeft, velocity);
- onDragCancelled(animView);
- if (targetLeft == 0) {
- handleMenuCoveredOrDismissed();
- }
- }
-
- @Override
- public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
- mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
- }
-
- public boolean isFalseGesture(MotionEvent ev) {
- return super.isFalseGesture(ev);
- }
-
- private void handleMenuCoveredOrDismissed() {
- if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
- mMenuExposedView = null;
- }
- }
-
- @Override
- public Animator getViewTranslationAnimator(View v, float target,
- AnimatorUpdateListener listener) {
- if (v instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
- } else {
- return super.getViewTranslationAnimator(v, target, listener);
- }
- }
-
- @Override
- public void setTranslation(View v, float translate) {
- ((ExpandableView) v).setTranslation(translate);
- }
-
- @Override
- public float getTranslation(View v) {
- return ((ExpandableView) v).getTranslation();
- }
-
- @Override
- public void dismiss(View animView, float velocity) {
- dismissChild(animView, velocity,
- !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
- }
-
- @Override
- public void snap(View animView, float targetLeft, float velocity) {
- snapChild(animView, targetLeft, velocity);
- }
-
- @Override
- public boolean swipedFarEnough(float translation, float viewSize) {
- return swipedFarEnough();
- }
-
- @Override
- public boolean swipedFastEnough(float translation, float velocity) {
- return swipedFastEnough();
- }
-
- @Override
- public float getMinDismissVelocity() {
- return getEscapeVelocity();
- }
-
- public void onMenuShown(View animView) {
- onDragCancelled(animView);
-
- // If we're on the lockscreen we want to false this.
- if (isAntiFalsingNeeded()) {
- mHandler.removeCallbacks(mFalsingCheck);
- mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
- }
- }
-
- public void closeControlsIfOutsideTouch(MotionEvent ev) {
- NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
- View view = null;
- if (guts != null && !guts.getGutsContent().isLeavebehind()) {
- // Only close visible guts if they're not a leavebehind.
- view = guts;
- } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
- && mTranslatingParentView != null) {
- // Checking menu
- view = mTranslatingParentView;
- }
- if (view != null && !isTouchInView(ev, view)) {
- // Touch was outside visible guts / menu notification, close what's visible
- mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
- false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- resetExposedMenuView(true /* animate */, true /* force */);
- }
- }
-
- public void resetExposedMenuView(boolean animate, boolean force) {
- if (mMenuExposedView == null
- || (!force && mMenuExposedView == mTranslatingParentView)) {
- // If no menu is showing or it's showing for this view we do nothing.
- return;
- }
- final View prevMenuExposedView = mMenuExposedView;
- if (animate) {
- Animator anim = getViewTranslationAnimator(prevMenuExposedView,
- 0 /* leftTarget */, null /* updateListener */);
- if (anim != null) {
- anim.start();
- }
- } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
- if (!row.isRemoved()) {
- row.resetTranslation();
- }
- }
- mMenuExposedView = null;
- }
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public boolean hasActiveNotifications() {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public boolean hasActiveNotifications() {
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
@@ -5578,8 +5195,8 @@
return mStatusBarState == StatusBarState.KEYGUARD;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void updateSpeedBumpIndex() {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void updateSpeedBumpIndex() {
int speedBumpIndex = 0;
int currentIndex = 0;
final int N = getChildCount();
@@ -5599,24 +5216,6 @@
updateSpeedBumpIndex(speedBumpIndex, noAmbient);
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private boolean isTouchInView(MotionEvent ev, View view) {
- if (view == null) {
- return false;
- }
- final int height = (view instanceof ExpandableView)
- ? ((ExpandableView) view).getActualHeight()
- : view.getHeight();
- final int rx = (int) ev.getRawX();
- final int ry = (int) ev.getRawY();
- view.getLocationOnScreen(mTempInt2);
- final int x = mTempInt2[0];
- final int y = mTempInt2[1];
- Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
- boolean ret = rect.contains(rx, ry);
- return ret;
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateContinuousShadowDrawing() {
boolean continuousShadowUpdate = mAnimationRunning
@@ -5640,11 +5239,29 @@
@ShadeViewRefactor(RefactorComponent.INPUT)
public void closeControlsIfOutsideTouch(MotionEvent ev) {
- mSwipeHelper.closeControlsIfOutsideTouch(ev);
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+ View translatingParentView = mSwipeHelper.getTranslatingParentView();
+ View view = null;
+ if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+ // Only close visible guts if they're not a leavebehind.
+ view = guts;
+ } else if (menuRow != null && menuRow.isMenuVisible()
+ && translatingParentView != null) {
+ // Checking menu
+ view = translatingParentView;
+ }
+ if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+ // Touch was outside visible guts / menu notification, close what's visible
+ mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ resetExposedMenuView(true /* animate */, true /* force */);
+ }
}
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- static class AnimationEvent {
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ static class AnimationEvent {
static AnimationFilter[] FILTERS = new AnimationFilter[]{
@@ -5944,8 +5561,8 @@
}
}
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private final StateListener mStateListener = new StateListener() {
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ private final StateListener mStateListener = new StateListener() {
@Override
public void onStatePreChange(int oldState, int newState) {
if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
@@ -5958,9 +5575,222 @@
setStatusBarState(newState);
}
- @Override
- public void onStatePostChange() {
+ @Override
+ public void onStatePostChange() {
NotificationStackScrollLayout.this.onStatePostChange();
}
- };
+ };
+
+ class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener {
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void onMenuClicked(View view, int x, int y, MenuItem item) {
+ if (mLongPressListener == null) {
+ return;
+ }
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
+ row.getStatusBarNotification().getPackageName());
+ }
+ mLongPressListener.onLongPress(view, x, y, item);
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void onMenuReset(View row) {
+ View translatingParentView = mSwipeHelper.getTranslatingParentView();
+ if (translatingParentView != null && row == translatingParentView) {
+ mSwipeHelper.clearExposedMenuView();
+ mSwipeHelper.clearTranslatingParentView();
+ }
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void onMenuShown(View row) {
+ if (row instanceof ExpandableNotificationRow) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
+ ((ExpandableNotificationRow) row).getStatusBarNotification()
+ .getPackageName());
+ }
+ mSwipeHelper.onMenuShown(row);
+ }
+ }
+
+ class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback {
+ @Override
+ public void onDismiss() {
+ mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ }
+
+ @Override
+ public void onSnooze(StatusBarNotification sbn,
+ NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
+ mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
+ }
+
+ @Override
+ public boolean isExpanded() {
+ return NotificationStackScrollLayout.this.isExpanded();
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void onDragCancelled(View v) {
+ mFalsingManager.onNotificatonStopDismissing();
+ setSwipingInProgress(false);
+ }
+
+ /**
+ * Handles cleanup after the given {@code view} has been fully swiped out (including
+ * re-invoking dismiss logic in case the notification has not made its way out yet).
+ */
+ @Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void onChildDismissed(View view) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (!row.isDismissed()) {
+ handleChildViewDismissed(view);
+ }
+ ViewGroup transientContainer = row.getTransientContainer();
+ if (transientContainer != null) {
+ transientContainer.removeTransientView(view);
+ }
+ }
+
+ /**
+ * Starts up notification dismiss and tells the notification, if any, to remove itself from
+ * layout.
+ *
+ * @param view view (e.g. notification) to dismiss from the layout
+ */
+
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void handleChildViewDismissed(View view) {
+ if (mDismissAllInProgress) {
+ return;
+ }
+
+ boolean isBlockingHelperShown = false;
+
+ setSwipingInProgress(false);
+ if (mDragAnimPendingChildren.contains(view)) {
+ // We start the swipe and finish it in the same frame; we don't want a drag
+ // animation.
+ mDragAnimPendingChildren.remove(view);
+ }
+ mAmbientState.onDragFinished(view);
+ updateContinuousShadowDrawing();
+
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (row.isHeadsUp()) {
+ mHeadsUpManager.addSwipedOutNotification(
+ row.getStatusBarNotification().getKey());
+ }
+ isBlockingHelperShown =
+ row.performDismissWithBlockingHelper(false /* fromAccessibility */);
+ }
+
+ if (!isBlockingHelperShown) {
+ mSwipedOutViews.add(view);
+ }
+ mFalsingManager.onNotificationDismissed();
+ if (mFalsingManager.shouldEnforceBouncer()) {
+ mStatusBar.executeRunnableDismissingKeyguard(
+ null,
+ null /* cancelAction */,
+ false /* dismissShade */,
+ true /* afterKeyguardGone */,
+ false /* deferred */);
+ }
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean isAntiFalsingNeeded() {
+ return onKeyguard();
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public View getChildAtPosition(MotionEvent ev) {
+ View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
+ ev.getY());
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ ExpandableNotificationRow parent = row.getNotificationParent();
+ if (parent != null && parent.areChildrenExpanded()
+ && (parent.areGutsExposed()
+ || mSwipeHelper.getExposedMenuView() == parent
+ || (parent.getNotificationChildren().size() == 1
+ && parent.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
+ // if we only have a single child.
+ child = parent;
+ }
+ }
+ return child;
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void onBeginDrag(View v) {
+ mFalsingManager.onNotificatonStartDismissing();
+ setSwipingInProgress(true);
+ mAmbientState.onBeginDrag(v);
+ updateContinuousShadowDrawing();
+ if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
+ mDragAnimPendingChildren.add(v);
+ mNeedsAnimation = true;
+ }
+ requestChildrenUpdate();
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void onChildSnappedBack(View animView, float targetLeft) {
+ mAmbientState.onDragFinished(animView);
+ updateContinuousShadowDrawing();
+ if (!mDragAnimPendingChildren.contains(animView)) {
+ if (mAnimationsEnabled) {
+ mSnappedBackChildren.add(animView);
+ mNeedsAnimation = true;
+ }
+ requestChildrenUpdate();
+ } else {
+ // We start the swipe and snap back in the same frame, we don't want any animation
+ mDragAnimPendingChildren.remove(animView);
+ }
+ NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+ if (menuRow != null && targetLeft == 0) {
+ menuRow.resetMenu();
+ mSwipeHelper.clearCurrentMenuRow();
+ }
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean updateSwipeProgress(View animView, boolean dismissable,
+ float swipeProgress) {
+ // Returning true prevents alpha fading.
+ return !mFadeNotificationsOnDismiss;
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public float getFalsingThresholdFactor() {
+ return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ }
+
+ @Override
+ public boolean canChildBeDismissed(View v) {
+ return NotificationStackScrollLayout.this.canChildBeDismissed(v);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
new file mode 100644
index 0000000..028957d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the Licen
+ */
+
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.SwipeHelper;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+
+@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT)
+class NotificationSwipeHelper extends SwipeHelper
+ implements NotificationSwipeActionHelper {
+ @VisibleForTesting
+ protected static final long COVER_MENU_DELAY = 4000;
+ private static final String TAG = "NotificationSwipeHelper";
+ private final Runnable mFalsingCheck;
+ private View mTranslatingParentView;
+ private View mMenuExposedView;
+ private final NotificationCallback mCallback;
+ private final NotificationMenuRowPlugin.OnMenuEventListener mMenuListener;
+
+ private static final long SWIPE_MENU_TIMING = 200;
+
+ private NotificationMenuRowPlugin mCurrMenuRow;
+
+ public NotificationSwipeHelper(int swipeDirection, NotificationCallback callback,
+ Context context, NotificationMenuRowPlugin.OnMenuEventListener menuListener) {
+ super(swipeDirection, callback, context);
+ mMenuListener = menuListener;
+ mCallback = callback;
+ mFalsingCheck = new Runnable() {
+ @Override
+ public void run() {
+ resetExposedMenuView(true /* animate */, true /* force */);
+ }
+ };
+ }
+
+ public View getTranslatingParentView() {
+ return mTranslatingParentView;
+ }
+
+ public void clearTranslatingParentView() { setTranslatingParentView(null); }
+
+ @VisibleForTesting
+ protected void setTranslatingParentView(View view) { mTranslatingParentView = view; };
+
+ public void setExposedMenuView(View view) {
+ mMenuExposedView = view;
+ }
+
+ public void clearExposedMenuView() { setExposedMenuView(null); }
+
+ public void clearCurrentMenuRow() { setCurrentMenuRow(null); }
+
+ public View getExposedMenuView() {
+ return mMenuExposedView;
+ }
+
+ public void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) {
+ mCurrMenuRow = menuRow;
+ }
+
+ public NotificationMenuRowPlugin getCurrentMenuRow() { return mCurrMenuRow; }
+
+ @VisibleForTesting
+ protected Handler getHandler() { return mHandler; }
+
+ @VisibleForTesting
+ protected Runnable getFalsingCheck() { return mFalsingCheck; };
+
+ @Override
+ public void onDownUpdate(View currView, MotionEvent ev) {
+ mTranslatingParentView = currView;
+ NotificationMenuRowPlugin menuRow = getCurrentMenuRow();
+ if (menuRow != null) {
+ menuRow.onTouchStart();
+ }
+ clearCurrentMenuRow();
+ getHandler().removeCallbacks(getFalsingCheck());
+
+ // Slide back any notifications that might be showing a menu
+ resetExposedMenuView(true /* animate */, false /* force */);
+
+ if (currView instanceof ExpandableNotificationRow) {
+ initializeRow((ExpandableNotificationRow) currView);
+ }
+ }
+
+ @VisibleForTesting
+ protected void initializeRow(ExpandableNotificationRow row) {
+ if (row.getEntry().hasFinishedInitialization()) {
+ mCurrMenuRow = row.createMenu();
+ mCurrMenuRow.setMenuClickListener(mMenuListener);
+ mCurrMenuRow.onTouchStart();
+ }
+ }
+
+ private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) {
+ return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu();
+ }
+
+ @Override
+ public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
+ getHandler().removeCallbacks(getFalsingCheck());
+ NotificationMenuRowPlugin menuRow = getCurrentMenuRow();
+ if (menuRow != null) {
+ menuRow.onTouchMove(delta);
+ }
+ }
+
+ @Override
+ public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+ float translation) {
+ NotificationMenuRowPlugin menuRow = getCurrentMenuRow();
+ if (menuRow != null) {
+ menuRow.onTouchEnd();
+ handleMenuRowSwipe(ev, animView, velocity, menuRow);
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ protected void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity,
+ NotificationMenuRowPlugin menuRow) {
+ if (!menuRow.shouldShowMenu()) {
+ // If the menu should not be shown, then there is no need to check if the a swipe
+ // should result in a snapping to the menu. As a result, just check if the swipe
+ // was enough to dismiss the notification.
+ if (isDismissGesture(ev)) {
+ dismiss(animView, velocity);
+ } else {
+ snapClosed(animView, velocity);
+ menuRow.onSnapClosed();
+ }
+ return;
+ }
+
+ if (menuRow.isSnappedAndOnSameSide()) {
+ // Menu was snapped to previously and we're on the same side
+ handleSwipeFromSnap(ev, animView, velocity, menuRow);
+ } else {
+ // Menu has not been snapped, or was snapped previously but is now on
+ // the opposite side.
+ handleSwipeFromNonSnap(ev, animView, velocity, menuRow);
+ }
+ }
+
+ private void handleSwipeFromNonSnap(MotionEvent ev, View animView, float velocity,
+ NotificationMenuRowPlugin menuRow) {
+ boolean isDismissGesture = isDismissGesture(ev);
+ final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity);
+ final boolean gestureFastEnough = getEscapeVelocity() <= Math.abs(velocity);
+
+ final double timeForGesture = ev.getEventTime() - ev.getDownTime();
+ final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed()
+ && timeForGesture >= SWIPE_MENU_TIMING;
+
+ if (!isFalseGesture(ev)
+ && (swipedEnoughToShowMenu(menuRow)
+ && (!gestureFastEnough || showMenuForSlowOnGoing))
+ || (gestureTowardsMenu && !isDismissGesture)) {
+ // Menu has not been snapped to previously and this is menu revealing gesture
+ snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+ menuRow.onSnapOpen();
+ } else if (isDismissGesture(ev) && !gestureTowardsMenu) {
+ dismiss(animView, velocity);
+ menuRow.onDismiss();
+ } else {
+ snapClosed(animView, velocity);
+ menuRow.onSnapClosed();
+ }
+ }
+
+ private void handleSwipeFromSnap(MotionEvent ev, View animView, float velocity,
+ NotificationMenuRowPlugin menuRow) {
+ boolean isDismissGesture = isDismissGesture(ev);
+
+ final boolean withinSnapMenuThreshold =
+ menuRow.isWithinSnapMenuThreshold();
+
+ if (withinSnapMenuThreshold && !isDismissGesture) {
+ // Haven't moved enough to unsnap from the menu
+ menuRow.onSnapOpen();
+ snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+ } else if (isDismissGesture && !menuRow.shouldSnapBack()) {
+ // Only dismiss if we're not moving towards the menu
+ dismiss(animView, velocity);
+ menuRow.onDismiss();
+ } else {
+ snapClosed(animView, velocity);
+ menuRow.onSnapClosed();
+ }
+ }
+
+ @Override
+ public void dismissChild(final View view, float velocity,
+ boolean useAccelerateInterpolator) {
+ superDismissChild(view, velocity, useAccelerateInterpolator);
+ if (mCallback.isExpanded()) {
+ // We don't want to quick-dismiss when it's a heads up as this might lead to closing
+ // of the panel early.
+ mCallback.handleChildViewDismissed(view);
+ }
+ mCallback.onDismiss();
+ handleMenuCoveredOrDismissed();
+ }
+
+ @VisibleForTesting
+ protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
+ super.dismissChild(view, velocity, useAccelerateInterpolator);
+ }
+
+ @VisibleForTesting
+ protected void superSnapChild(final View animView, final float targetLeft, float velocity) {
+ super.snapChild(animView, targetLeft, velocity);
+ }
+
+ @Override
+ public void snapChild(final View animView, final float targetLeft, float velocity) {
+ superSnapChild(animView, targetLeft, velocity);
+ mCallback.onDragCancelled(animView);
+ if (targetLeft == 0) {
+ handleMenuCoveredOrDismissed();
+ }
+ }
+
+ @Override
+ public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+ mCallback.onSnooze(sbn, snoozeOption);
+ }
+
+ @VisibleForTesting
+ protected void handleMenuCoveredOrDismissed() {
+ View exposedMenuView = getExposedMenuView();
+ if (exposedMenuView != null && exposedMenuView == mTranslatingParentView) {
+ clearExposedMenuView();
+ }
+ }
+
+ @VisibleForTesting
+ protected Animator superGetViewTranslationAnimator(View v, float target,
+ ValueAnimator.AnimatorUpdateListener listener) {
+ return super.getViewTranslationAnimator(v, target, listener);
+ }
+
+ @Override
+ public Animator getViewTranslationAnimator(View v, float target,
+ ValueAnimator.AnimatorUpdateListener listener) {
+ if (v instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
+ } else {
+ return superGetViewTranslationAnimator(v, target, listener);
+ }
+ }
+
+ @Override
+ public void setTranslation(View v, float translate) {
+ if (v instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) v).setTranslation(translate);
+ } else {
+ Log.wtf(TAG, "setTranslation should only be called on an ExpandableNotificationRow.");
+ }
+ }
+
+ @Override
+ public float getTranslation(View v) {
+ if (v instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) v).getTranslation();
+ }
+ else {
+ Log.wtf(TAG, "getTranslation should only be called on an ExpandableNotificationRow.");
+ return 0f;
+ }
+ }
+
+ @Override
+ public boolean swipedFastEnough(float translation, float viewSize) {
+ return swipedFastEnough();
+ }
+
+ @Override
+ @VisibleForTesting
+ protected boolean swipedFastEnough() {
+ return super.swipedFastEnough();
+ }
+
+ @Override
+ public boolean swipedFarEnough(float translation, float viewSize) {
+ return swipedFarEnough();
+ }
+
+ @Override
+ @VisibleForTesting
+ protected boolean swipedFarEnough() {
+ return super.swipedFarEnough();
+ }
+
+ @Override
+ public void dismiss(View animView, float velocity) {
+ dismissChild(animView, velocity,
+ !swipedFastEnough() /* useAccelerateInterpolator */);
+ }
+
+ @Override
+ public void snapOpen(View animView, int targetLeft, float velocity) {
+ snapChild(animView, targetLeft, velocity);
+ }
+
+ @VisibleForTesting
+ protected void snapClosed(View animView, float velocity) {
+ snapChild(animView, 0, velocity);
+ }
+
+ @Override
+ @VisibleForTesting
+ protected float getEscapeVelocity() {
+ return super.getEscapeVelocity();
+ }
+
+ @Override
+ public float getMinDismissVelocity() {
+ return getEscapeVelocity();
+ }
+
+ public void onMenuShown(View animView) {
+ setExposedMenuView(getTranslatingParentView());
+ mCallback.onDragCancelled(animView);
+ Handler handler = getHandler();
+
+ // If we're on the lockscreen we want to false this.
+ if (mCallback.isAntiFalsingNeeded()) {
+ handler.removeCallbacks(getFalsingCheck());
+ handler.postDelayed(getFalsingCheck(), COVER_MENU_DELAY);
+ }
+ }
+
+ @VisibleForTesting
+ protected boolean shouldResetMenu(boolean force) {
+ if (mMenuExposedView == null
+ || (!force && mMenuExposedView == mTranslatingParentView)) {
+ // If no menu is showing or it's showing for this view we do nothing.
+ return false;
+ }
+ return true;
+ }
+
+ public void resetExposedMenuView(boolean animate, boolean force) {
+ if (!shouldResetMenu(force)) {
+ return;
+ }
+ final View prevMenuExposedView = getExposedMenuView();
+ if (animate) {
+ Animator anim = getViewTranslationAnimator(prevMenuExposedView,
+ 0 /* leftTarget */, null /* updateListener */);
+ if (anim != null) {
+ anim.start();
+ }
+ } else if (prevMenuExposedView instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) prevMenuExposedView;
+ if (!row.isRemoved()) {
+ row.resetTranslation();
+ }
+ }
+ clearExposedMenuView();
+ }
+
+ public static boolean isTouchInView(MotionEvent ev, View view) {
+ if (view == null) {
+ return false;
+ }
+ final int height = (view instanceof ExpandableView)
+ ? ((ExpandableView) view).getActualHeight()
+ : view.getHeight();
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
+ int[] temp = new int[2];
+ view.getLocationOnScreen(temp);
+ final int x = temp[0];
+ final int y = temp[1];
+ Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
+ boolean ret = rect.contains(rx, ry);
+ return ret;
+ }
+
+ public interface NotificationCallback extends SwipeHelper.Callback{
+ boolean isExpanded();
+
+ void handleChildViewDismissed(View view);
+
+ void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption);
+
+ void onDismiss();
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
new file mode 100644
index 0000000..c7ab27b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.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.systemui.statusbar.phone;
+
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+import android.content.Context;
+import android.view.View;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.statusbar.policy.KeyButtonView;
+
+/**
+ * Simple contextual button that is added to the {@link ContextualButtonGroup}. Extend if need extra
+ * functionality.
+ */
+public class ContextualButton extends ButtonDispatcher {
+
+ protected final @DrawableRes int mIconResId;
+
+ /**
+ * Create a contextual button that will use a {@link KeyButtonView} and
+ * {@link KeyButtonDrawable} get and show the button from xml to its icon drawable.
+ * @param buttonResId the button view from xml layout
+ * @param iconResId icon resource to be used
+ */
+ public ContextualButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
+ super(buttonResId);
+ mIconResId = iconResId;
+ }
+
+ /**
+ * Reload the drawable from resource id, should reapply the previous dark intensity.
+ */
+ public void updateIcon() {
+ final KeyButtonDrawable currentDrawable = getImageDrawable();
+ KeyButtonDrawable drawable = getNewDrawable();
+ if (currentDrawable != null) {
+ drawable.setDarkIntensity(currentDrawable.getDarkIntensity());
+ }
+ setImageDrawable(drawable);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ // Stop any active animations if hidden
+ final KeyButtonDrawable currentDrawable = getImageDrawable();
+ if (visibility != View.VISIBLE && currentDrawable != null && currentDrawable.canAnimate()) {
+ currentDrawable.clearAnimationCallbacks();
+ currentDrawable.resetAnimation();
+ }
+ }
+
+ protected KeyButtonDrawable getNewDrawable() {
+ return KeyButtonDrawable.create(getContext().getApplicationContext(), mIconResId,
+ false /* shadow */);
+ }
+
+ /**
+ * This context is from the view that could be stale after rotation or config change. To get
+ * correct resources use getApplicationContext() as well.
+ * @return current view context
+ */
+ protected Context getContext() {
+ return getCurrentView().getContext();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
new file mode 100644
index 0000000..1b03966
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.IdRes;
+import android.annotation.NonNull;
+import android.view.View;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContextualButtonGroup extends ButtonDispatcher {
+ private static final int INVALID_INDEX = -1;
+
+ // List of pairs that contains the button and if the button was visible within this group
+ private final List<ButtonData> mButtonData = new ArrayList<>();
+
+ public ContextualButtonGroup(@IdRes int containerId) {
+ super(containerId);
+ }
+
+ /**
+ * Add a contextual button to the group. The order of adding increases in its priority. The
+ * priority is used to determine which button should be visible when setting multiple button's
+ * visibility {@see setButtonVisiblity}.
+ * @param button the button added to the group
+ */
+ public void addButton(@NonNull ContextualButton button) {
+ mButtonData.add(new ButtonData(button));
+ }
+
+ public ContextualButton getContextButton(@IdRes int buttonResId) {
+ int index = getContextButtonIndex(buttonResId);
+ if (index != INVALID_INDEX) {
+ return mButtonData.get(index).button;
+ }
+ return null;
+ }
+
+ public ContextualButton getVisibleContextButton() {
+ for (int i = mButtonData.size() - 1; i >= 0; --i) {
+ if (mButtonData.get(i).markedVisible) {
+ return mButtonData.get(i).button;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the visibility of the button by {@param buttonResId} with {@param visible}. Only one
+ * button is shown at a time. The input button will only show up if it has higher priority than
+ * a previous button, otherwise it will be marked as visible and shown later if all higher
+ * priority buttons are invisible. Therefore hiding a button will show the next marked visible
+ * button. This group's view will be visible if at least one button is visible.
+ * @return if the button is visible after operation
+ * @throws RuntimeException if the input id does not match any of the ids in the group
+ */
+ public int setButtonVisiblity(@IdRes int buttonResId, boolean visible) {
+ final int index = getContextButtonIndex(buttonResId);
+ if (index == INVALID_INDEX) {
+ throw new RuntimeException("Cannot find the button id of " + buttonResId
+ + " in context group");
+ }
+ setVisibility(View.INVISIBLE);
+ mButtonData.get(index).markedVisible = visible;
+
+ // Make all buttons invisible except the first markedVisible button
+ boolean alreadyFoundVisibleButton = false;
+ int i = mButtonData.size() - 1;
+ for (; i >= 0; --i) {
+ final ButtonData buttonData = mButtonData.get(i);
+ if (!alreadyFoundVisibleButton && buttonData.markedVisible) {
+ buttonData.setVisibility(View.VISIBLE);
+ setVisibility(View.VISIBLE);
+ alreadyFoundVisibleButton = true;
+ } else {
+ buttonData.setVisibility(View.INVISIBLE);
+ }
+ }
+ return mButtonData.get(index).button.getVisibility();
+ }
+
+ /**
+ * See if button is group visible. Group visible determines if a button can be visible when
+ * higher priority buttons go invisible.
+ * @param buttonResId the button to see if it is group visible
+ * @return true if button is group visible
+ */
+ public boolean isButtonVisibleWithinGroup(@IdRes int buttonResId) {
+ final int index = getContextButtonIndex(buttonResId);
+ return index != INVALID_INDEX && mButtonData.get(index).markedVisible;
+ }
+
+ /**
+ * Update all the icons that are attached to this group. This will get all the buttons to update
+ * their icons for their buttons.
+ */
+ public void updateIcons() {
+ for (ButtonData data : mButtonData) {
+ data.button.updateIcon();
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("ContextualButtonGroup {");
+ pw.println(" getVisibleContextButton(): " + getVisibleContextButton());
+ pw.println(" isVisible(): " + isVisible());
+ pw.println(" mButtonData [ ");
+ for (int i = mButtonData.size() - 1; i >= 0; --i) {
+ final ButtonData data = mButtonData.get(i);
+ pw.println(" " + i + ": markedVisible=" + data.markedVisible
+ + " visible=" + data.button.getVisibility()
+ + " alpha=" + data.button.getAlpha());
+ }
+ pw.println(" ]");
+ pw.println(" }");
+ }
+
+ private int getContextButtonIndex(@IdRes int buttonResId) {
+ for (int i = 0; i < mButtonData.size(); ++i) {
+ if (mButtonData.get(i).button.getId() == buttonResId) {
+ return i;
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ private final static class ButtonData {
+ ContextualButton button;
+ boolean markedVisible;
+
+ ButtonData(ContextualButton button) {
+ this.button = button;
+ this.markedVisible = false;
+ }
+
+ void setVisibility(int visiblity) {
+ button.setVisibility(visiblity);
+ }
+ }
+}
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 0d3ba77..25db4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -63,9 +63,14 @@
if (!mDozing) {
return;
}
- mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
- mHandler.postDelayed(mPulseOutExtended,
- mDozeParameters.getPulseVisibleDurationExtended());
+ // All pulses except notifications should time out on their own. Pulses due to
+ // notifications should instead be managed externally based off the notification's
+ // lifetime.
+ if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) {
+ mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+ mHandler.postDelayed(mPulseOutExtended,
+ mDozeParameters.getPulseVisibleDurationExtended());
+ }
mFullyPulsing = true;
}
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 4a05989..cfc3271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -177,7 +177,7 @@
mReleaseOnExpandFinish = false;
} else {
for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (contains(entry.key)) {
+ if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
}
@@ -345,7 +345,7 @@
public void onReorderingAllowed() {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (contains(entry.key)) {
+ if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index d89bcda..f667726 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -19,6 +19,7 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
@@ -171,7 +172,6 @@
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mDozing;
private int mIndicationBottomMargin;
- private int mIndicationBottomMarginAmbient;
private float mDarkAmount;
private int mBurnInXOffset;
private int mBurnInYOffset;
@@ -246,10 +246,8 @@
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
- mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom_ambient);
mBurnInYOffset = getResources().getDimensionPixelSize(
- R.dimen.charging_indication_burn_in_prevention_offset_y);
+ R.dimen.default_burn_in_prevention_offset);
updateCameraVisibility();
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
@@ -320,10 +318,8 @@
super.onConfigurationChanged(newConfig);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
- mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom_ambient);
mBurnInYOffset = getResources().getDimensionPixelSize(
- R.dimen.charging_indication_burn_in_prevention_offset_y);
+ R.dimen.default_burn_in_prevention_offset);
MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
if (mlp.bottomMargin != mIndicationBottomMargin) {
mlp.bottomMargin = mIndicationBottomMargin;
@@ -567,9 +563,7 @@
return;
}
mDarkAmount = darkAmount;
- mIndicationArea.setAlpha(MathUtils.lerp(1f, 0.7f, darkAmount));
- mIndicationArea.setTranslationY(MathUtils.lerp(0,
- mIndicationBottomMargin - mIndicationBottomMarginAmbient, darkAmount));
+ mIndicationArea.setAlpha(1f - darkAmount);
}
private static boolean isSuccessfulLaunch(int result) {
@@ -842,10 +836,10 @@
public void dozeTimeTick() {
if (mDarkAmount == 1) {
- // Move indication every minute to avoid burn-in
- int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
- int burnInYOffset = (int) (-mBurnInYOffset + Math.random() * mBurnInYOffset * 2);
- mIndicationArea.setTranslationY(dozeTranslation + burnInYOffset);
+ // Move views every minute to avoid burn-in
+ int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
+ - mBurnInYOffset;
+ mLockIcon.setTranslationY(burnInYOffset);
}
}
@@ -854,7 +848,7 @@
return;
}
mBurnInXOffset = burnInXOffset;
- mIndicationArea.setTranslationX(burnInXOffset);
+ mLockIcon.setTranslationX(burnInXOffset);
}
private class DefaultLeftButton implements IntentButton {
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 042e4ff..33bc164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
import android.content.res.Resources;
@@ -30,10 +31,6 @@
*/
public class KeyguardClockPositionAlgorithm {
- private static final long MILLIS_PER_MINUTES = 1000 * 60;
- private static final float BURN_IN_PREVENTION_PERIOD_Y = 521;
- private static final float BURN_IN_PREVENTION_PERIOD_X = 83;
-
/**
* How much the clock height influences the shade position.
* 0 means nothing, 1 means move the shade up by the height of the clock
@@ -228,34 +225,15 @@
}
private float burnInPreventionOffsetY() {
- return zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES,
- mBurnInPreventionOffsetY * 2,
- BURN_IN_PREVENTION_PERIOD_Y)
+ return getBurnInOffset(mBurnInPreventionOffsetY * 2, false /* xAxis */)
- mBurnInPreventionOffsetY;
}
private float burnInPreventionOffsetX() {
- return zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES,
- mBurnInPreventionOffsetX * 2,
- BURN_IN_PREVENTION_PERIOD_X)
+ return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */)
- mBurnInPreventionOffsetX;
}
- /**
- * Implements a continuous, piecewise linear, periodic zig-zag function
- *
- * Can be thought of as a linear approximation of abs(sin(x)))
- *
- * @param period period of the function, ie. zigzag(x + period) == zigzag(x)
- * @param amplitude maximum value of the function
- * @return a value between 0 and amplitude
- */
- private float zigzag(float x, float amplitude, float period) {
- float xprime = (x % period) / (period / 2);
- float interpolationAmount = (xprime <= 1) ? xprime : (2 - xprime);
- return interpolate(0, amplitude, interpolationAmount);
- }
-
public static class Result {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 5630da6..7c84df9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import android.annotation.ColorInt;
import android.content.Context;
@@ -70,16 +71,18 @@
private static final int LAYOUT_CUTOUT = 1;
private static final int LAYOUT_NO_CUTOUT = 2;
+ private final Rect mEmptyRect = new Rect(0, 0, 0, 0);
+
private boolean mShowPercentAvailable;
private boolean mBatteryCharging;
private boolean mKeyguardUserSwitcherShowing;
private boolean mBatteryListening;
private TextView mCarrierLabel;
- private View mSystemIconsSuperContainer;
private MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
+ private StatusIconContainer mStatusIconContainer;
private BatteryController mBatteryController;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
@@ -99,6 +102,18 @@
*/
private int mCutoutSideNudge = 0;
+ /**
+ * How much to move icons to avoid burn in.
+ */
+ private int mBurnInOffset;
+ private int mCurrentBurnInOffsetX;
+ private int mCurrentBurnInOffsetY;
+
+ /**
+ * Ratio representing being in ambient mode or not.
+ */
+ private float mDarkAmount;
+
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -113,6 +128,7 @@
mBatteryView = mSystemIconsContainer.findViewById(R.id.battery);
mCutoutSpace = findViewById(R.id.cutout_space_view);
mStatusIconArea = findViewById(R.id.status_icon_area);
+ mStatusIconContainer = findViewById(R.id.statusIcons);
loadDimens();
updateUserSwitcher();
@@ -169,6 +185,8 @@
R.dimen.system_icons_super_container_avatarless_margin_end);
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
+ mBurnInOffset = getResources().getDimensionPixelSize(
+ R.dimen.default_burn_in_prevention_offset);
mShowPercentAvailable = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_battery_percentage_setting_available);
}
@@ -440,6 +458,14 @@
}
public void onThemeChanged() {
+ mBatteryView.setColorsFromContext(mContext);
+ updateIconsAndTextColors();
+ // Reload user avatar
+ ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
+ .onDensityOrFontScaleChanged();
+ }
+
+ private void updateIconsAndTextColors() {
@ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
@ColorInt int iconColor = Utils.getColorStateListDefaultColor(mContext,
@@ -448,14 +474,9 @@
float intensity = textColor == Color.WHITE ? 0 : 1;
mCarrierLabel.setTextColor(iconColor);
mIconManager.setTint(iconColor);
- mBatteryView.setColorsFromContext(mContext);
- Rect tintArea = new Rect(0, 0, 0, 0);
- applyDarkness(R.id.battery, tintArea, intensity, iconColor);
- applyDarkness(R.id.clock, tintArea, intensity, iconColor);
- // Reload user avatar
- ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
- .onDensityOrFontScaleChanged();
+ applyDarkness(R.id.battery, mEmptyRect, intensity * (1f - mDarkAmount), iconColor);
+ applyDarkness(R.id.clock, mEmptyRect, intensity, iconColor);
}
private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
@@ -475,4 +496,32 @@
mBatteryView.dump(fd, pw, args);
}
}
+
+ public void setDarkAmount(float darkAmount) {
+ mDarkAmount = darkAmount;
+ if (darkAmount == 0) {
+ dozeTimeTick();
+ }
+ updateDozeState();
+ }
+
+ public void dozeTimeTick() {
+ mCurrentBurnInOffsetX = getBurnInOffset(mBurnInOffset, true /* xAxis */);
+ mCurrentBurnInOffsetY = getBurnInOffset(mBurnInOffset, false /* xAxis */);
+ updateDozeState();
+ }
+
+ private void updateDozeState() {
+ float alpha = 1f - mDarkAmount;
+ int visibility = alpha != 0f ? VISIBLE : INVISIBLE;
+ mCarrierLabel.setAlpha(alpha);
+ mCarrierLabel.setVisibility(visibility);
+ mStatusIconContainer.setAlpha(alpha);
+ mStatusIconContainer.setVisibility(visibility);
+
+ mSystemIconsContainer.setTranslationX(-mCurrentBurnInOffsetX * mDarkAmount);
+ mSystemIconsContainer.setTranslationY(mCurrentBurnInOffsetY * mDarkAmount);
+ updateIconsAndTextColors();
+ }
+
}
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 66486ce..9c579da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -35,6 +35,7 @@
import android.animation.ObjectAnimator;
import android.annotation.IdRes;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.Fragment;
import android.app.IActivityManager;
@@ -88,8 +89,8 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -284,7 +285,7 @@
mNavigationBarView = (NavigationBarView) view;
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
- mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());
+ mNavigationBarView.setComponents(mStatusBar.getPanel());
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
if (savedInstanceState != null) {
@@ -463,7 +464,7 @@
style = rotationCCW ? R.style.RotateButtonCCWStart0 :
R.style.RotateButtonCWStart0;
}
- mNavigationBarView.updateRotateSuggestionButtonStyle(style, true);
+ mNavigationBarView.updateRotateSuggestionButtonStyle(style);
}
if (mNavigationBarWindowState != WINDOW_STATE_SHOWING) {
@@ -946,7 +947,7 @@
private boolean onLongPressRecents() {
if (mRecents == null || !ActivityTaskManager.supportsMultiWindow(getContext())
|| !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
- || Recents.getConfiguration().isLowRamDevice
+ || ActivityManager.isLowRamDeviceStatic()
// If we are connected to the overview service, then disable the recents button
|| mOverviewProxyService.getProxy() != null) {
return false;
@@ -1110,7 +1111,7 @@
}
};
- class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
+ class TaskStackListenerImpl extends TaskStackChangeListener {
// Invalidate any rotation suggestion on task change or activity orientation change
// Note: all callbacks happen on main thread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
deleted file mode 100644
index 8c02e1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ /dev/null
@@ -1,295 +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.phone;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_TOP;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.tuner.TunerService;
-
-/**
- * Class to detect gestures on the navigation bar.
- */
-public class NavigationBarGestureHelper implements TunerService.Tunable, GestureHelper {
-
- private static final String TAG = "NavBarGestureHelper";
- private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture";
- /**
- * When dragging from the navigation bar, we drag in recents.
- */
- public static final int DRAG_MODE_NONE = -1;
-
- /**
- * When dragging from the navigation bar, we drag in recents.
- */
- public static final int DRAG_MODE_RECENTS = 0;
-
- /**
- * When dragging from the navigation bar, we drag the divider.
- */
- public static final int DRAG_MODE_DIVIDER = 1;
-
- private RecentsComponent mRecentsComponent;
- private Divider mDivider;
- private Context mContext;
- private NavigationBarView mNavigationBarView;
- private boolean mIsVertical;
-
- private final QuickStepController mQuickStepController;
- private final int mScrollTouchSlop;
- private final StatusBar mStatusBar;
- private int mTouchDownX;
- private int mTouchDownY;
- private boolean mDownOnRecents;
- private VelocityTracker mVelocityTracker;
-
- private boolean mDockWindowEnabled;
- private boolean mDockWindowTouchSlopExceeded;
- private int mDragMode;
-
- public NavigationBarGestureHelper(Context context) {
- mContext = context;
- mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
- Resources r = context.getResources();
- mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
- mQuickStepController = new QuickStepController(context);
- Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
- }
-
- public void destroy() {
- Dependency.get(TunerService.class).removeTunable(this);
- }
-
- public void setComponents(RecentsComponent recentsComponent, Divider divider,
- NavigationBarView navigationBarView) {
- mRecentsComponent = recentsComponent;
- mDivider = divider;
- mNavigationBarView = navigationBarView;
- mQuickStepController.setComponents(mNavigationBarView);
- }
-
- public void setBarState(boolean isVertical, boolean isRTL) {
- mIsVertical = isVertical;
- mQuickStepController.setBarState(isVertical, isRTL);
- }
-
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if (!canHandleGestures()) {
- return false;
- }
- boolean result = mQuickStepController.onInterceptTouchEvent(event);
- if (mDockWindowEnabled) {
- result |= interceptDockWindowEvent(event);
- }
- return result;
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- if (!canHandleGestures()) {
- return false;
- }
- boolean result = mQuickStepController.onTouchEvent(event);
- if (mDockWindowEnabled) {
- result |= handleDockWindowEvent(event);
- }
- return result;
- }
-
- public void onDraw(Canvas canvas) {
- mQuickStepController.onDraw(canvas);
- }
-
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mQuickStepController.onLayout(changed, left, top, right, bottom);
- }
-
- public void onDarkIntensityChange(float intensity) {
- mQuickStepController.onDarkIntensityChange(intensity);
- }
-
- public void onNavigationButtonLongPress(View v) {
- mQuickStepController.onNavigationButtonLongPress(v);
- }
-
- private boolean interceptDockWindowEvent(MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- handleDragActionDownEvent(event);
- break;
- case MotionEvent.ACTION_MOVE:
- return handleDragActionMoveEvent(event);
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- handleDragActionUpEvent(event);
- break;
- }
- return false;
- }
-
- private boolean handleDockWindowEvent(MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- handleDragActionDownEvent(event);
- break;
- case MotionEvent.ACTION_MOVE:
- handleDragActionMoveEvent(event);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- handleDragActionUpEvent(event);
- break;
- }
- return true;
- }
-
- private void handleDragActionDownEvent(MotionEvent event) {
- mVelocityTracker = VelocityTracker.obtain();
- mVelocityTracker.addMovement(event);
- mDockWindowTouchSlopExceeded = false;
- mTouchDownX = (int) event.getX();
- mTouchDownY = (int) event.getY();
-
- if (mNavigationBarView != null) {
- View recentsButton = mNavigationBarView.getRecentsButton().getCurrentView();
- if (recentsButton != null) {
- mDownOnRecents = mTouchDownX >= recentsButton.getLeft()
- && mTouchDownX <= recentsButton.getRight()
- && mTouchDownY >= recentsButton.getTop()
- && mTouchDownY <= recentsButton.getBottom();
- } else {
- mDownOnRecents = false;
- }
- }
- }
-
- private boolean handleDragActionMoveEvent(MotionEvent event) {
- mVelocityTracker.addMovement(event);
- int x = (int) event.getX();
- int y = (int) event.getY();
- int xDiff = Math.abs(x - mTouchDownX);
- int yDiff = Math.abs(y - mTouchDownY);
- if (mDivider == null || mRecentsComponent == null) {
- return false;
- }
- if (!mDockWindowTouchSlopExceeded) {
- boolean touchSlopExceeded = !mIsVertical
- ? yDiff > mScrollTouchSlop && yDiff > xDiff
- : xDiff > mScrollTouchSlop && xDiff > yDiff;
- if (mDownOnRecents && touchSlopExceeded
- && mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) {
- Rect initialBounds = null;
- int dragMode = calculateDragMode();
- int createMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- if (dragMode == DRAG_MODE_DIVIDER) {
- initialBounds = new Rect();
- mDivider.getView().calculateBoundsForPosition(mIsVertical
- ? (int) event.getRawX()
- : (int) event.getRawY(),
- mDivider.getView().isHorizontalDivision()
- ? DOCKED_TOP
- : DOCKED_LEFT,
- initialBounds);
- } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
- < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
- createMode = SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
- }
- boolean docked = mRecentsComponent.splitPrimaryTask(dragMode, createMode,
- initialBounds, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
- if (docked) {
- mDragMode = dragMode;
- if (mDragMode == DRAG_MODE_DIVIDER) {
- mDivider.getView().startDragging(false /* animate */, true /* touching*/);
- }
- mDockWindowTouchSlopExceeded = true;
- return true;
- }
- }
- } else {
- if (mDragMode == DRAG_MODE_DIVIDER) {
- int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX();
- SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm()
- .calculateSnapTarget(position, 0f /* velocity */, false /* hardDismiss */);
- mDivider.getView().resizeStack(position, snapTarget.position, snapTarget);
- } else if (mDragMode == DRAG_MODE_RECENTS) {
- mRecentsComponent.onDraggingInRecents(event.getRawY());
- }
- }
- return false;
- }
-
- private void handleDragActionUpEvent(MotionEvent event) {
- mVelocityTracker.addMovement(event);
- mVelocityTracker.computeCurrentVelocity(1000);
- if (mDockWindowTouchSlopExceeded && mDivider != null && mRecentsComponent != null) {
- if (mDragMode == DRAG_MODE_DIVIDER) {
- mDivider.getView().stopDragging(mIsVertical
- ? (int) event.getRawX()
- : (int) event.getRawY(),
- mIsVertical
- ? mVelocityTracker.getXVelocity()
- : mVelocityTracker.getYVelocity(),
- true /* avoidDismissStart */, false /* logMetrics */);
- } else if (mDragMode == DRAG_MODE_RECENTS) {
- mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
- }
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- private boolean canHandleGestures() {
- return !mStatusBar.isKeyguardShowing();
- }
-
- private int calculateDragMode() {
- if (mIsVertical && !mDivider.getView().isHorizontalDivision()) {
- return DRAG_MODE_DIVIDER;
- }
- if (!mIsVertical && mDivider.getView().isHorizontalDivision()) {
- return DRAG_MODE_DIVIDER;
- }
- return DRAG_MODE_RECENTS;
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- switch (key) {
- case KEY_DOCK_WINDOW_GESTURE:
- mDockWindowEnabled = newValue != null && (Integer.parseInt(newValue) != 0);
- break;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index e6f2c33..52134d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -37,7 +37,7 @@
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider;
import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout;
import com.android.systemui.statusbar.policy.KeyButtonView;
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 9d13ea2..16b2987 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -36,17 +36,13 @@
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
-import androidx.annotation.ColorInt;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
-import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -59,7 +55,6 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
@@ -68,12 +63,11 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavGesture;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsOnboarding;
-import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -106,10 +100,7 @@
boolean mVertical;
private int mCurrentRotation = -1;
- boolean mShowMenu;
- boolean mShowAccessibilityButton;
boolean mLongClickableAccessibilityButton;
- boolean mShowRotateButton;
int mDisabledFlags = 0;
int mNavigationIconHints = 0;
@@ -125,10 +116,6 @@
private KeyButtonDrawable mHomeDefaultIcon;
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
- private KeyButtonDrawable mImeIcon;
- private KeyButtonDrawable mMenuIcon;
- private KeyButtonDrawable mAccessibilityIcon;
- private KeyButtonDrawable mRotateSuggestionIcon;
private GestureHelper mGestureHelper;
private final DeadZone mDeadZone;
@@ -151,16 +138,13 @@
private boolean mDockedStackExists;
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
+ private final ContextualButtonGroup mContextualButtonGroup;
private Configuration mConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
- private RecentsComponent mRecentsComponent;
- private Divider mDivider;
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
- private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
-
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
* occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
@@ -279,17 +263,30 @@
Context.WINDOW_SERVICE)).getDefaultDisplay();
mVertical = false;
- mShowMenu = false;
-
- mShowAccessibilityButton = false;
mLongClickableAccessibilityButton = false;
+ // Set up the context group of buttons
+ mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
+ final ContextualButton menuButton = new ContextualButton(R.id.menu,
+ R.drawable.ic_sysbar_menu);
+ final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
+ R.drawable.ic_ime_switcher_default);
+ final RotationContextButton rotateSuggestionButton = new RotationContextButton(
+ R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button,
+ R.style.RotateButtonCCWStart90);
+ final ContextualButton accessibilityButton =
+ new ContextualButton(R.id.accessibility_button,
+ R.drawable.ic_sysbar_accessibility_button);
+ mContextualButtonGroup.addButton(menuButton);
+ mContextualButtonGroup.addButton(imeSwitcherButton);
+ mContextualButtonGroup.addButton(rotateSuggestionButton);
+ mContextualButtonGroup.addButton(accessibilityButton);
+
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
- reloadNavIcons();
mScreenPinningNotify = new ScreenPinningNotify(mContext);
mBarTransitions = new NavigationBarTransitions(this);
@@ -297,14 +294,11 @@
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
- mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));
- mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
- mButtonDispatchers.put(R.id.accessibility_button,
- new ButtonDispatcher(R.id.accessibility_button));
- mButtonDispatchers.put(R.id.rotate_suggestion,
- new ButtonDispatcher(R.id.rotate_suggestion));
- mButtonDispatchers.put(R.id.menu_container,
- new ButtonDispatcher(R.id.menu_container));
+ mButtonDispatchers.put(R.id.menu, menuButton);
+ mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
+ mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
+ mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
+ mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
mDeadZone = new DeadZone(this);
}
@@ -316,14 +310,10 @@
return mBarTransitions.getLightTransitionsController();
}
- public void setComponents(RecentsComponent recentsComponent, Divider divider,
- NotificationPanelView panel) {
- mRecentsComponent = recentsComponent;
- mDivider = divider;
+ public void setComponents(NotificationPanelView panel) {
mPanelView = panel;
- if (mGestureHelper instanceof NavigationBarGestureHelper) {
- ((NavigationBarGestureHelper) mGestureHelper).setComponents(
- recentsComponent, divider, this);
+ if (mGestureHelper instanceof QuickStepController) {
+ ((QuickStepController) mGestureHelper).setComponents(this);
}
}
@@ -432,10 +422,6 @@
return mButtonDispatchers.get(R.id.rotate_suggestion);
}
- public ButtonDispatcher getMenuContainer() {
- return mButtonDispatchers.get(R.id.menu_container);
- }
-
public SparseArray<ButtonDispatcher> getButtonDispatchers() {
return mButtonDispatchers;
}
@@ -473,14 +459,7 @@
}
if (densityChange || dirChange) {
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
- mMenuIcon = getDrawable(R.drawable.ic_sysbar_menu);
-
- mAccessibilityIcon = getDrawable(R.drawable.ic_sysbar_accessibility_button,
- false /* hasShadow */);
-
- mImeIcon = getDrawable(R.drawable.ic_ime_switcher_default, false /* hasShadow */);
-
- updateRotateSuggestionButtonStyle(mRotateBtnStyle, false);
+ mContextualButtonGroup.updateIcons();
}
if (orientationChange || densityChange || dirChange) {
mBackIcon = getBackDrawable();
@@ -538,19 +517,11 @@
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
- return getDrawable(mContext, icon, true /* hasShadow */);
+ return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */);
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) {
- return getDrawable(mContext, icon, hasShadow);
- }
-
- private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon, boolean hasShadow) {
- final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
- final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
- Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
- Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
- return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow);
+ return KeyButtonDrawable.create(mContext, icon, hasShadow);
}
@Override
@@ -609,29 +580,17 @@
updateRecentsIcon();
// Update IME button visibility, a11y and rotate button always overrides the appearance
- final boolean showImeButton =
- !mShowAccessibilityButton &&
- !mShowRotateButton &&
- ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
- getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
- getImeSwitchButton().setImageDrawable(mImeIcon);
- updateContextualContainerVisibility();
-
- // Update menu button, visibility logic in method
- setMenuVisibility(mShowMenu, true);
- getMenuButton().setImageDrawable(mMenuIcon);
-
- // Update rotate button, visibility altered by a11y button logic
- getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
-
- // Update a11y button, visibility logic in state method
- setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
- getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
+ mContextualButtonGroup.setButtonVisiblity(R.id.ime_switcher,
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
mBarTransitions.reapplyDarkIntensity();
boolean disableHome = ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
+ // TODO(b/113914868): investigation log for disappearing home button
+ Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
+ + " mDisabledFlags=" + mDisabledFlags);
+
// Always disable recents when alternate car mode UI is active.
boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
@@ -782,88 +741,28 @@
}
public void setMenuVisibility(final boolean show) {
- setMenuVisibility(show, false);
+ mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
}
- public void setMenuVisibility(final boolean show, final boolean force) {
- if (!force && mShowMenu == show) return;
-
- mShowMenu = show;
-
- // Only show Menu if IME switcher, rotate and Accessibility buttons are not shown.
- final boolean shouldShow = mShowMenu &&
- !mShowAccessibilityButton &&
- !mShowRotateButton &&
- ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
-
- getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
- updateContextualContainerVisibility();
+ public void updateRotateSuggestionButtonStyle(@StyleRes int style) {
+ RotationContextButton button = (RotationContextButton) mContextualButtonGroup
+ .getContextButton(R.id.rotate_suggestion);
+ button.setStyle(style);
+ button.updateIcon();
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
- mShowAccessibilityButton = visible;
mLongClickableAccessibilityButton = longClickable;
- if (visible) {
- // Accessibility button overrides Menu, IME switcher and rotate buttons.
- setMenuVisibility(false, true);
- getImeSwitchButton().setVisibility(View.INVISIBLE);
- setRotateButtonVisibility(false);
- }
-
- getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getAccessibilityButton().setLongClickable(longClickable);
- updateContextualContainerVisibility();
- }
-
- public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) {
- mRotateBtnStyle = style;
- final Context ctx = getContext();
-
- // Use the supplied style to set the icon's rotation parameters
- Context rotateContext = new ContextThemeWrapper(ctx, style);
-
- // Recreate the icon and set it if needed
- float previousIntensity = mRotateSuggestionIcon != null
- ? mRotateSuggestionIcon.getDarkIntensity() : 0;
- mRotateSuggestionIcon = getDrawable(rotateContext, R.drawable.ic_sysbar_rotate_button,
- false /* hasShadow */);
- mRotateSuggestionIcon.setDarkIntensity(previousIntensity);
-
- if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
+ mContextualButtonGroup.setButtonVisiblity(R.id.accessibility_button, visible);
}
public int setRotateButtonVisibility(boolean visible) {
- // Never show if a11y is visible
- final boolean adjVisible = visible && !mShowAccessibilityButton;
- final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
-
- // No need to do anything if the request matches the current state
- if (vis == getRotateSuggestionButton().getVisibility()) return vis;
-
- getRotateSuggestionButton().setVisibility(vis);
- mShowRotateButton = visible;
- updateContextualContainerVisibility();
-
- // Stop any active animations if hidden
- if (!visible && mRotateSuggestionIcon.canAnimate()) {
- mRotateSuggestionIcon.clearAnimationCallbacks();
- mRotateSuggestionIcon.resetAnimation();
- }
-
- // Hide/restore other button visibility, if necessary
- updateNavButtonIcons();
-
- // Return applied visibility
- return vis;
+ return mContextualButtonGroup.setButtonVisiblity(R.id.rotate_suggestion, visible);
}
- public boolean isRotateButtonVisible() { return mShowRotateButton; }
-
- private void updateContextualContainerVisibility() {
- // Only show the menu container when one of its buttons are visible
- getMenuContainer().setVisibility((mShowAccessibilityButton || mShowRotateButton || mShowMenu
- || (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0)
- ? VISIBLE : INVISIBLE);
+ public boolean isRotateButtonVisible() {
+ return getRotateSuggestionButton().isVisible();
}
void hideRecentsOnboarding() {
@@ -897,6 +796,7 @@
DockedStackExistsListener.register(mDockedListener);
updateRotatedViews();
+ reloadNavIcons();
}
public void onDarkIntensityChange(float intensity) {
@@ -998,7 +898,6 @@
// force the low profile & disabled states into compliance
mBarTransitions.init();
- setMenuVisibility(mShowMenu, true /* force */);
if (DEBUG) {
Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
@@ -1170,8 +1069,8 @@
@Override
public void onPluginDisconnected(NavGesture plugin) {
- NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext());
- defaultHelper.setComponents(mRecentsComponent, mDivider, this);
+ QuickStepController defaultHelper = new QuickStepController(getContext());
+ defaultHelper.setComponents(this);
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
@@ -1204,17 +1103,20 @@
pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
mDisabledFlags,
mVertical ? "true" : "false",
- mShowMenu ? "true" : "false"));
+ getMenuButton().isVisible() ? "true" : "false"));
dumpButton(pw, "back", getBackButton());
dumpButton(pw, "home", getHomeButton());
dumpButton(pw, "rcnt", getRecentsButton());
dumpButton(pw, "menu", getMenuButton());
+ dumpButton(pw, "rota", getRotateSuggestionButton());
dumpButton(pw, "a11y", getAccessibilityButton());
- mRecentsOnboarding.dump(pw);
-
pw.println(" }");
+
+ mContextualButtonGroup.dump(pw);
+ mGestureHelper.dump(pw);
+ mRecentsOnboarding.dump(pw);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
index 09833d4..1524f80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
@@ -95,6 +95,9 @@
}
private View findNearestChild(MotionEvent event) {
+ if (mClickableChildren.isEmpty()) {
+ return null;
+ }
return mClickableChildren
.stream()
.filter(v -> v.isAttachedToWindow())
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 6b6566c..c08366a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.NonNull;
import android.app.Notification;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
@@ -23,6 +24,9 @@
import android.util.Log;
import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -43,15 +47,18 @@
/**
* A class to handle notifications and their corresponding groups.
*/
-public class NotificationGroupManager implements OnHeadsUpChangedListener {
+public class NotificationGroupManager implements OnHeadsUpChangedListener,
+ OnAmbientChangedListener {
private static final String TAG = "NotificationGroupManager";
- private static final long HEADS_UP_TRANSFER_TIMEOUT = 300;
+ private static final long ALERT_TRANSFER_TIMEOUT = 300;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private OnGroupChangeListener mListener;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
+ private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+ private boolean mIsDozing;
private boolean mIsUpdatingUnchangedGroup;
private HashMap<String, NotificationData.Entry> mPendingNotifications;
@@ -162,40 +169,58 @@
mListener.onGroupCreatedFromChildren(group);
}
}
- cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */);
+ cleanUpAlertStatesOnAdd(group, false /* addIsPending */);
}
public void onPendingEntryAdded(NotificationData.Entry shadeEntry) {
String groupKey = getGroupKey(shadeEntry.notification);
NotificationGroup group = mGroupMap.get(groupKey);
if (group != null) {
- cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */);
+ cleanUpAlertStatesOnAdd(group, true /* addIsPending */);
}
}
/**
- * Clean up the heads up states when a new child was added.
+ * Set whether or not the device is dozing. This allows the group manager to reset some
+ * specific alert state logic based off when the state changes.
+ * @param isDozing if the device is dozing.
+ */
+ public void setDozing(boolean isDozing) {
+ if (mIsDozing != isDozing) {
+ for (NotificationGroup group : mGroupMap.values()) {
+ group.lastAlertTransfer = 0;
+ group.alertSummaryOnNextAddition = false;
+ }
+ }
+ mIsDozing = isDozing;
+ }
+
+ /**
+ * Clean up the alert states when a new child was added.
* @param group The group where a view was added or will be added.
* @param addIsPending True if is the addition still pending or false has it already been added.
*/
- private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) {
- if (!addIsPending && group.hunSummaryOnNextAddition) {
- if (!mHeadsUpManager.contains(group.summary.key)) {
- mHeadsUpManager.showNotification(group.summary);
+ private void cleanUpAlertStatesOnAdd(NotificationGroup group, boolean addIsPending) {
+
+ AlertingNotificationManager alertManager =
+ mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
+ if (!addIsPending && group.alertSummaryOnNextAddition) {
+ if (!alertManager.isAlerting(group.summary.key)) {
+ alertManager.showNotification(group.summary);
}
- group.hunSummaryOnNextAddition = false;
+ group.alertSummaryOnNextAddition = false;
}
// Because notification groups are not delivered as a whole unit, it may happen that a
// group child gets added quite a bit after the summary got posted. Our guidance is, that
// apps should always post the group summary as well and we'll hide it for them if the child
- // is the only child in a group. Because of this, we also have to transfer heads up to the
- // child, otherwise the invisible summary would be heads-upped.
+ // is the only child in a group. Because of this, we also have to transfer alert to the
+ // child, otherwise the invisible summary would be alerted.
// This transfer to the child is not always correct in case the app has just posted another
// child in addition to the existing one, but it hasn't arrived in systemUI yet. In such
- // a scenario we would transfer the heads up to the old child and the wrong notification
- // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the
+ // a scenario we would transfer the alert to the old child and the wrong notification
+ // would be alerted. In order to avoid this, we'll recover from this issue and alert the
// summary again instead of the old child if it's within a certain timeout.
- if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) {
+ if (SystemClock.elapsedRealtime() - group.lastAlertTransfer < ALERT_TRANSFER_TIMEOUT) {
if (!onlySummaryAlerts(group.summary)) {
return;
}
@@ -215,26 +240,24 @@
int size = children.size();
for (int i = 0; i < size; i++) {
NotificationData.Entry entry = children.get(i);
- if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) {
+ if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.key, true /* releaseImmediately */);
+ alertManager.removeNotification(entry.key, true /* releaseImmediately */);
}
}
if (isolatedChild != null && onlySummaryAlerts(isolatedChild)
- && isolatedChild.row.isHeadsUp()) {
+ && alertManager.isAlerting(isolatedChild.key)) {
releasedChild = true;
- mHeadsUpManager.removeNotification(
- isolatedChild.key, true /* releaseImmediately */);
+ alertManager.removeNotification(isolatedChild.key, true /* releaseImmediately */);
}
- if (releasedChild && !mHeadsUpManager.contains(group.summary.key)) {
+ if (releasedChild && !alertManager.isAlerting(group.summary.key)) {
boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
if (notifyImmediately) {
- mHeadsUpManager.showNotification(group.summary);
+ alertManager.showNotification(group.summary);
} else {
- group.hunSummaryOnNextAddition = true;
+ group.alertSummaryOnNextAddition = true;
}
- group.lastHeadsUpTransfer = 0;
+ group.lastAlertTransfer = 0;
}
}
}
@@ -264,8 +287,8 @@
}
private void onEntryBecomingChild(NotificationData.Entry entry) {
- if (entry.row.isHeadsUp()) {
- onHeadsUpStateChanged(entry, true);
+ if (shouldIsolate(entry)) {
+ isolateNotification(entry);
}
}
@@ -281,7 +304,11 @@
&& hasIsolatedChildren(group)));
if (prevSuppressed != group.suppressed) {
if (group.suppressed) {
- handleSuppressedSummaryHeadsUpped(group.summary);
+ if (mHeadsUpManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
+ } else if (mAmbientPulseManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, mAmbientPulseManager);
+ }
}
if (!mIsUpdatingUnchangedGroup && mListener != null) {
mListener.onGroupsChanged();
@@ -495,54 +522,56 @@
}
@Override
+ public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+ onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
+ }
+
+ @Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ }
+
+ private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting,
+ AlertingNotificationManager alertManager) {
final StatusBarNotification sbn = entry.notification;
- if (entry.row.isHeadsUp()) {
- if (shouldIsolate(sbn)) {
- // We will be isolated now, so lets update the groups
- onEntryRemovedInternal(entry, entry.notification);
-
- mIsolatedEntries.put(sbn.getKey(), sbn);
-
- onEntryAdded(entry);
- // We also need to update the suppression of the old group, because this call comes
- // even before the groupManager knows about the notification at all.
- // When the notification gets added afterwards it is already isolated and therefore
- // it doesn't lead to an update.
- updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
- mListener.onGroupsChanged();
- } else {
- handleSuppressedSummaryHeadsUpped(entry);
+ if (isAlerting) {
+ if (shouldIsolate(entry)) {
+ isolateNotification(entry);
+ } else if (sbn.getNotification().isGroupSummary()
+ && isGroupSuppressed(sbn.getGroupKey())){
+ handleSuppressedSummaryAlerted(entry, alertManager);
}
} else {
- if (mIsolatedEntries.containsKey(sbn.getKey())) {
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.notification);
- mIsolatedEntries.remove(sbn.getKey());
- onEntryAdded(entry);
- mListener.onGroupsChanged();
- }
+ stopIsolatingNotification(entry);
}
}
- private void handleSuppressedSummaryHeadsUpped(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
+ /**
+ * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * summary should for all intents and purposes be invisible to the user and as a result should
+ * not alert. When this is the case, it is our responsibility to pass the alert to the
+ * appropriate child which will be the representative notification alerting for the group.
+ * @param summary the summary that is suppressed and alerting
+ * @param alertManager the alert manager that manages the alerting summary
+ */
+ private void handleSuppressedSummaryAlerted(@NonNull NotificationData.Entry summary,
+ @NonNull AlertingNotificationManager alertManager) {
+ StatusBarNotification sbn = summary.notification;
if (!isGroupSuppressed(sbn.getGroupKey())
|| !sbn.getNotification().isGroupSummary()
- || !entry.row.isHeadsUp()) {
+ || !alertManager.isAlerting(sbn.getKey())) {
return;
}
- // The parent of a suppressed group got huned, lets hun the child!
+ // The parent of a suppressed group got alerted, lets alert the child!
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- if (pendingInflationsWillAddChildren(notificationGroup)) {
- // New children will actually be added to this group, let's not transfer the heads
- // up
- return;
- }
-
if (notificationGroup != null) {
+ if (pendingInflationsWillAddChildren(notificationGroup)) {
+ // New children will actually be added to this group, let's not transfer the alert.
+ return;
+ }
+
Iterator<NotificationData.Entry> iterator
= notificationGroup.children.values().iterator();
NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null;
@@ -551,20 +580,35 @@
}
if (child != null) {
if (child.row.keepInParent() || child.row.isRemoved() || child.row.isDismissed()) {
- // the notification is actually already removed, no need to do heads-up on it.
+ // the notification is actually already removed, no need to do alert on it.
return;
}
- if (mHeadsUpManager.contains(child.key)) {
- mHeadsUpManager.updateNotification(child.key, true /* alert */);
- } else {
- if (onlySummaryAlerts(entry)) {
- notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime();
- }
- mHeadsUpManager.showNotification(child);
- }
+ transferAlertStateToChild(summary, child, alertManager);
}
}
- mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */);
+ }
+
+ /**
+ * Transfers the alert state from a given summary notification to the specified child. The
+ * result is the child will now alert while the summary does not.
+ *
+ * @param summary the currently alerting summary notification
+ * @param child the child that should receive the alert
+ * @param alertManager the manager for the alert
+ */
+ private void transferAlertStateToChild(@NonNull NotificationData.Entry summary,
+ @NonNull NotificationData.Entry child,
+ @NonNull AlertingNotificationManager alertManager) {
+ NotificationGroup notificationGroup = mGroupMap.get(summary.notification.getGroupKey());
+ if (alertManager.isAlerting(child.key)) {
+ alertManager.updateNotification(child.key, true /* alert */);
+ } else {
+ if (onlySummaryAlerts(summary)) {
+ notificationGroup.lastAlertTransfer = SystemClock.elapsedRealtime();
+ }
+ alertManager.showNotification(child);
+ }
+ alertManager.removeNotification(summary.key, true /* releaseImmediately */);
}
private boolean onlySummaryAlerts(NotificationData.Entry entry) {
@@ -596,13 +640,69 @@
return false;
}
- private boolean shouldIsolate(StatusBarNotification sbn) {
+ /**
+ * Whether a notification that is normally part of a group should be temporarily isolated from
+ * the group and put in their own group visually. This generally happens when the notification
+ * is alerting.
+ *
+ * @param entry the notification to check
+ * @return true if the entry should be isolated
+ */
+
+ private boolean shouldIsolate(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
- && (sbn.getNotification().fullScreenIntent != null
- || notificationGroup == null
- || !notificationGroup.expanded
- || isGroupNotFullyVisible(notificationGroup));
+ if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
+ return false;
+ }
+ if (!mIsDozing && !mHeadsUpManager.isAlerting(entry.key)) {
+ return false;
+ }
+ if (mIsDozing && !mAmbientPulseManager.isAlerting(entry.key)) {
+ return false;
+ }
+ return (sbn.getNotification().fullScreenIntent != null
+ || notificationGroup == null
+ || !notificationGroup.expanded
+ || isGroupNotFullyVisible(notificationGroup));
+ }
+
+ /**
+ * Isolate a notification from its group so that it visually shows as its own group.
+ *
+ * @param entry the notification to isolate
+ */
+ private void isolateNotification(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ // We will be isolated now, so lets update the groups
+ onEntryRemovedInternal(entry, entry.notification);
+
+ mIsolatedEntries.put(sbn.getKey(), sbn);
+
+ onEntryAdded(entry);
+ // We also need to update the suppression of the old group, because this call comes
+ // even before the groupManager knows about the notification at all.
+ // When the notification gets added afterwards it is already isolated and therefore
+ // it doesn't lead to an update.
+ updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
+ mListener.onGroupsChanged();
+ }
+
+ /**
+ * Stop isolating a notification and re-group it with its original logical group.
+ *
+ * @param entry the notification to un-isolate
+ */
+ private void stopIsolatingNotification(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+ if (mIsolatedEntries.containsKey(sbn.getKey())) {
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.notification);
+ mIsolatedEntries.remove(sbn.getKey());
+ onEntryAdded(entry);
+ mListener.onGroupsChanged();
+ }
}
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
@@ -641,11 +741,11 @@
*/
public boolean suppressed;
/**
- * The time when the last heads transfer from group to child happened, while the summary
- * has the flags to heads up on its own.
+ * The time when the last alert transfer from group to child happened, while the summary
+ * has the flags to alert up on its own.
*/
- public long lastHeadsUpTransfer;
- public boolean hunSummaryOnNextAddition;
+ public long lastAlertTransfer;
+ public boolean alertSummaryOnNextAddition;
@Override
public String toString() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
index 9ff907b..9e561d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
@@ -24,7 +24,7 @@
import com.android.systemui.plugins.NotificationListenerController;
import com.android.systemui.plugins.NotificationListenerController.NotificationProvider;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import java.util.ArrayList;
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 e5e5d40..3a4c218 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -63,6 +63,7 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.QSFragment;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -455,6 +456,10 @@
initBottomArea();
setDarkAmount(mLinearDarkAmount, mInterpolatedDarkAmount);
+ if (mKeyguardStatusBar != null) {
+ mKeyguardStatusBar.onThemeChanged();
+ }
+
setKeyguardStatusViewVisibility(mBarState, false, false);
setKeyguardBottomAreaVisibility(mBarState, false);
}
@@ -1836,10 +1841,10 @@
return;
}
float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
- mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
- * mKeyguardStatusBarAnimateAlpha);
- mKeyguardStatusBar.setVisibility(mKeyguardStatusBar.getAlpha() != 0f
- && !mDozing ? VISIBLE : INVISIBLE);
+ float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
+ * mKeyguardStatusBarAnimateAlpha;
+ mKeyguardStatusBar.setAlpha(newAlpha);
+ mKeyguardStatusBar.setVisibility(newAlpha != 0f ? VISIBLE : INVISIBLE);
}
private void updateKeyguardBottomAreaAlpha() {
@@ -2347,16 +2352,7 @@
}
private void updateDozingVisibilities(boolean animate) {
- if (mDozing) {
- mKeyguardStatusBar.setVisibility(View.INVISIBLE);
- mKeyguardBottomArea.setDozing(mDozing, animate);
- } else {
- mKeyguardStatusBar.setVisibility(View.VISIBLE);
- mKeyguardBottomArea.setDozing(mDozing, animate);
- if (animate) {
- animateKeyguardStatusBarIn(DOZE_ANIMATION_DURATION);
- }
- }
+ mKeyguardBottomArea.setDozing(mDozing, animate);
}
@Override
@@ -2749,6 +2745,9 @@
}
});
mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
+ if (mQs instanceof QSFragment) {
+ mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
+ }
updateQsExpansion();
}
@@ -2811,6 +2810,7 @@
private void setDarkAmount(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
+ mKeyguardStatusBar.setDarkAmount(mInterpolatedDarkAmount);
mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
@@ -2837,6 +2837,7 @@
}
public void dozeTimeTick() {
+ mKeyguardStatusBar.dozeTimeTick();
mKeyguardStatusView.dozeTimeTick();
mKeyguardBottomArea.dozeTimeTick();
if (mInterpolatedDarkAmount > 0) {
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 a900c14..553165b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -67,8 +67,8 @@
import com.android.systemui.UiOffloadThread;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -595,16 +595,29 @@
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
mContext.getString(R.string.instant_apps));
mCurrentNotifs.add(new Pair<>(pkg, userId));
- String message = mContext.getString(R.string.instant_apps_message);
+
+ String helpUrl = mContext.getString(R.string.instant_apps_help_url);
+ boolean hasHelpUrl = !helpUrl.isEmpty();
+ String message = mContext.getString(hasHelpUrl
+ ? R.string.instant_apps_message_with_help
+ : R.string.instant_apps_message);
+
UserHandle user = UserHandle.of(userId);
PendingIntent appInfoAction = PendingIntent.getActivityAsUser(mContext, 0,
new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", pkg, null)), 0, null, user);
Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info),
appInfoAction).build();
+ PendingIntent helpCenterIntent = hasHelpUrl
+ ? PendingIntent.getActivityAsUser(mContext, 0,
+ new Intent(Intent.ACTION_VIEW).setData(Uri.parse(
+ helpUrl)),
+ 0, null, user)
+ : null;
Intent browserIntent = getTaskIntent(taskId, userId);
- Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL);
+ Notification.Builder builder = new Notification.Builder(mContext,
+ NotificationChannels.GENERAL);
if (browserIntent != null && browserIntent.isWebIntent()) {
// Make sure that this doesn't resolve back to an instant app
browserIntent.setComponent(null)
@@ -632,7 +645,8 @@
PendingIntent webPendingIntent = PendingIntent.getActivityAsUser(mContext, 0,
goToWebIntent, 0, null, user);
- Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
+ Action webAction = new Notification.Action.Builder(null,
+ mContext.getString(R.string.go_to_web),
webPendingIntent).build();
builder.addAction(webAction);
}
@@ -640,13 +654,15 @@
noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, builder
.addExtras(extras)
.addAction(action)
- .setContentIntent(appInfoAction)
+ .setContentIntent(helpCenterIntent)
.setColor(mContext.getColor(R.color.instant_apps_color))
- .setContentTitle(appInfo.loadLabel(mContext.getPackageManager()))
+ .setContentTitle(mContext.getString(R.string.instant_apps_title,
+ appInfo.loadLabel(mContext.getPackageManager())))
.setLargeIcon(Icon.createWithResource(pkg, appInfo.icon))
.setSmallIcon(Icon.createWithResource(mContext.getPackageName(),
R.drawable.instant_icon))
.setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setOngoing(true)
.build(),
new UserHandle(userId));
@@ -781,7 +797,7 @@
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
- private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() {
+ private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
// Listen for changes to stacks and then check which instant apps are foreground.
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 30e6afa..bce52a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -58,10 +58,12 @@
import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.NavigationBarCompat;
+import java.io.PrintWriter;
/**
* Class to detect gestures on the navigation bar and implement quick scrub.
@@ -117,6 +119,7 @@
private final int mTrackEndPadding;
private final int mHomeBackGestureDragLimit;
private final Context mContext;
+ private final StatusBar mStatusBar;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
private final Paint mTrackPaint = new Paint();
@@ -195,6 +198,7 @@
public QuickStepController(Context context) {
final Resources res = context.getResources();
mContext = context;
+ mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
@@ -218,6 +222,10 @@
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mStatusBar.isKeyguardShowing()) {
+ // Disallow any handling when the keyguard is showing
+ return false;
+ }
return handleTouchEvent(event);
}
@@ -227,6 +235,11 @@
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mStatusBar.isKeyguardShowing()) {
+ // Disallow any handling when the keyguard is showing
+ return false;
+ }
+
// The same down event was just sent on intercept and therefore can be ignored here
final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
&& mOverviewEventSender.getProxy() != null;
@@ -483,6 +496,21 @@
mHandler.removeCallbacksAndMessages(null);
}
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("QuickStepController {");
+ pw.print(" "); pw.println("mQuickScrubActive=" + mQuickScrubActive);
+ pw.print(" "); pw.println("mQuickStepStarted=" + mQuickStepStarted);
+ pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
+ pw.print(" "); pw.println("mBackGestureActive=" + mBackGestureActive);
+ pw.print(" "); pw.println("mCanPerformBack=" + mCanPerformBack);
+ pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
+ pw.print(" "); pw.println("mIsVertical=" + mIsVertical);
+ pw.print(" "); pw.println("mIsRTL=" + mIsRTL);
+ pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
+ pw.println("}");
+ }
+
private void startQuickStep(MotionEvent event) {
if (mIsInScreenPinning) {
mNavigationBarView.showPinningEscapeToast();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
new file mode 100644
index 0000000..15e189c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DrawableRes;
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.annotation.StyleRes;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.util.Utils;
+
+public class RotationContextButton extends ContextualButton {
+
+ private @StyleRes int mStyleRes;
+
+ public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId,
+ @StyleRes int style) {
+ super(buttonResId, iconResId);
+ mStyleRes = style;
+ }
+
+ public void setStyle(@StyleRes int styleRes) {
+ mStyleRes = styleRes;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ // Start the rotation animation once it becomes visible
+ final KeyButtonDrawable currentDrawable = getImageDrawable();
+ if (visibility == View.VISIBLE && currentDrawable != null) {
+ currentDrawable.resetAnimation();
+ currentDrawable.startAnimation();
+ }
+ }
+
+ @Override
+ protected KeyButtonDrawable getNewDrawable() {
+ Context context = new ContextThemeWrapper(getContext().getApplicationContext(), mStyleRes);
+ return KeyButtonDrawable.create(context, mIconResId, false /* shadow */);
+ }
+}
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 759e8ea..c3b87af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -102,6 +102,8 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -154,6 +156,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
@@ -179,14 +182,11 @@
import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
-import com.android.systemui.recents.events.activity.UndockingTaskEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.AppOpsListener;
import com.android.systemui.statusbar.BackDropView;
@@ -254,7 +254,8 @@
ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter,
- StatusBarStateController.StateListener {
+ StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener,
+ ActivityLaunchAnimator.Callback {
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -380,8 +381,6 @@
// settings
private QSPanel mQSPanel;
- // top bar
- private KeyguardStatusBarView mKeyguardStatusBar;
KeyguardIndicationController mKeyguardIndicationController;
// RemoteInputView to be activated after unlock
@@ -635,6 +634,8 @@
mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
mDisplay = mWindowManager.getDefaultDisplay();
updateDisplaySize();
@@ -804,7 +805,6 @@
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
R.id.notification_container_parent));
- mKeyguardStatusBar = mStatusBarWindow.findViewById(R.id.keyguard_header);
mNotificationIconAreaController = SystemUIFactory.getInstance()
.createNotificationIconAreaController(context, this);
@@ -865,6 +865,8 @@
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
+ mAmbientPulseManager.addListener(this);
+ mAmbientPulseManager.addListener(mGroupManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
@@ -977,7 +979,6 @@
((QSFragment) qs).setHost(qsh);
mQSPanel = ((QSFragment) qs).getQsPanel();
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
- mKeyguardStatusBar.setQSPanel(mQSPanel);
}
});
}
@@ -1115,8 +1116,6 @@
@Override
public void onThemeChanged() {
- // The status bar on the keyguard is a special layout.
- if (mKeyguardStatusBar != null) mKeyguardStatusBar.onThemeChanged();
// Recreate Indication controller because internal references changed
mKeyguardIndicationController =
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
@@ -1151,7 +1150,8 @@
protected void createUserSwitcher() {
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mKeyguardStatusBar,
+ mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
+ mStatusBarWindow.findViewById(R.id.keyguard_header),
mNotificationPanel);
}
@@ -1168,6 +1168,8 @@
mScrimController, this, UnlockMethodCache.getInstance(mContext));
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
+ //TODO: Can we put the keyguard view manager in Dependency?
+ mLockscreenUserManager.setKeyguardViewManager(mStatusBarKeyguardViewManager);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
@@ -1213,17 +1215,18 @@
int createMode = navbarPos == NAV_BAR_POS_LEFT
? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
: SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,
- null, metricsDockAction);
+ return mRecents.splitPrimaryTask(createMode, null, metricsDockAction);
} else {
Divider divider = getComponent(Divider.class);
- if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
- // Undocking from the minimized state is not supported
- return false;
- } else {
- EventBus.getDefault().send(new UndockingTaskEvent());
- if (metricsUndockAction != -1) {
- mMetricsLogger.action(metricsUndockAction);
+ if (divider != null) {
+ if (divider.isMinimized() && !divider.isHomeStackResizable()) {
+ // Undocking from the minimized state is not supported
+ return false;
+ } else {
+ divider.onUndockingTask();
+ if (metricsUndockAction != -1) {
+ mMetricsLogger.action(metricsUndockAction);
+ }
}
}
}
@@ -1233,7 +1236,7 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mHeadsUpManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -1697,8 +1700,12 @@
}
@Override
- public boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
- if (mIsOccluded && !isDozing()) {
+ public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+ if (isDozing()) {
+ return false;
+ }
+
+ if (mIsOccluded) {
boolean devicePublic = mLockscreenUserManager.
isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
@@ -1711,17 +1718,14 @@
if (!panelsEnabled()) {
if (DEBUG) {
- Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+ Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
}
return false;
}
if (sbn.getNotification().fullScreenIntent != null) {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
- return false;
- } else if (isDozing()) {
- // We never want heads up when we are dozing.
+ if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
return false;
} else {
// we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
@@ -1797,9 +1801,16 @@
@Override
public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
mEntryManager.updateNotificationRanking(null /* rankingMap */);
+ }
- if (isHeadsUp) {
- mDozeServiceHost.fireNotificationHeadsUp();
+ @Override
+ public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+ mEntryManager.updateNotificationRanking(null);
+ if (isAmbient) {
+ mDozeServiceHost.fireNotificationPulse();
+ } else if (!mAmbientPulseManager.hasNotifications()) {
+ // There are no longer any notifications to show. We should end the pulse now.
+ mDozeScrimController.pulseOutNow();
}
}
@@ -1900,6 +1911,7 @@
}
}
+ @Override
public void onLaunchAnimationCancelled() {
if (!isCollapsing()) {
onClosingFinished();
@@ -1910,6 +1922,31 @@
return mHeadsUpAppearanceController.shouldBeVisible();
}
+ @Override
+ public void onExpandAnimationFinished(boolean launchIsFullScreen) {
+ if (!isCollapsing()) {
+ onClosingFinished();
+ }
+ if (launchIsFullScreen) {
+ instantCollapseNotificationPanel();
+ }
+ }
+
+ @Override
+ public void onExpandAnimationTimedOut() {
+ if (isPresenterFullyCollapsed() && !isCollapsing()
+ && !mActivityLaunchAnimator.isLaunchForActivity()) {
+ onClosingFinished();
+ } else {
+ collapsePanel(true /* animate */);
+ }
+ }
+
+ @Override
+ public boolean areLaunchAnimationsEnabled() {
+ return mState == StatusBarState.SHADE;
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2145,7 +2182,7 @@
}
@Override
- public void animateExpandSettingsPanel(String subPanel) {
+ public void animateExpandSettingsPanel(@Nullable String subPanel) {
if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
if (!panelsEnabled()) {
return;
@@ -2154,7 +2191,6 @@
// Settings are not available in setup
if (!mUserSetup) return;
-
if (subPanel != null) {
mQSPanel.openDetails(subPanel);
}
@@ -2913,8 +2949,6 @@
// End old BaseStatusBar.userSwitched
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
animateCollapsePanels();
- updatePublicMode();
- mEntryManager.getNotificationData().filterAndSort();
if (mReinflateNotificationsOnUserSwitched) {
mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
mReinflateNotificationsOnUserSwitched = false;
@@ -3347,7 +3381,9 @@
}
public boolean isCollapsing() {
- return mNotificationPanel.isCollapsing() || mActivityLaunchAnimator.isAnimationPending();
+ return mNotificationPanel.isCollapsing()
+ || mActivityLaunchAnimator.isAnimationPending()
+ || mActivityLaunchAnimator.isAnimationRunning();
}
public void addPostCollapseAction(Runnable r) {
@@ -3458,7 +3494,7 @@
// notify listeners.
// If the state didn't change, we may still need to update public mode
- updatePublicMode();
+ mLockscreenUserManager.updatePublicMode();
mEntryManager.updateNotifications();
}
View viewToClick = null;
@@ -3546,34 +3582,6 @@
mScrimController.setExpansionAffectsAlpha(true);
}
- // TODO: Move this to NotificationLockscreenUserManager.
- private void updatePublicMode() {
- final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
- final boolean devicePublic = showingKeyguard
- && mStatusBarKeyguardViewManager.isSecure(
- mLockscreenUserManager.getCurrentUserId());
-
- // Look for public mode users. Users are considered public in either case of:
- // - device keyguard is shown in secure mode;
- // - profile is locked with a work challenge.
- SparseArray<UserInfo> currentProfiles = mLockscreenUserManager.getCurrentProfiles();
- for (int i = currentProfiles.size() - 1; i >= 0; i--) {
- final int userId = currentProfiles.valueAt(i).id;
- boolean isProfilePublic = devicePublic;
- if (!devicePublic && userId != mLockscreenUserManager.getCurrentUserId()) {
- // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
- // due to a race condition where this code could be called before
- // TrustManagerService updates its internal records, resulting in an incorrect
- // state being cached in mLockscreenPublicMode. (b/35951989)
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
- && mStatusBarKeyguardViewManager.isSecure(userId)) {
- isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
- }
- }
- mLockscreenUserManager.setLockscreenPublicMode(isProfilePublic, userId);
- }
- }
-
/**
* Switches theme from light to dark and vice-versa.
*/
@@ -3603,6 +3611,7 @@
mKeyguardIndicationController.setDozing(mDozing);
mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
mNotificationLogger.setDozing(mDozing);
+ mGroupManager.setDozing(mDozing);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -3763,8 +3772,6 @@
}
}
updateDozingState();
- updatePublicMode();
- mEntryManager.updateNotifications();
checkBarModes();
updateScrimController();
updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
@@ -4017,8 +4024,6 @@
@Override
public void onWorkChallengeChanged() {
- updatePublicMode();
- mEntryManager.updateNotifications();
if (mPendingWorkRemoteInputView != null
&& !mLockscreenUserManager.isAnyProfilePublicMode()) {
// Expand notification panel and the notification row, then click on remote input view
@@ -4149,6 +4154,7 @@
@Override
public void onStartedWakingUp() {
mDeviceInteractive = true;
+ mAmbientPulseManager.releaseAllImmediately();
mVisualStabilityManager.setScreenOn(true);
mNotificationPanel.setTouchAndAnimationDisabled(false);
mDozeServiceHost.stopDozing();
@@ -4181,11 +4187,7 @@
public void onScreenTurnedOff() {
mFalsingManager.onScreenOff();
mScrimController.onScreenTurnedOff();
- // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
- // in that case destroys the HeadsUpManager state, so don't do it in that case.
- if (!isPulsing()) {
- updateIsKeyguard();
- }
+ updateIsKeyguard();
}
};
@@ -4245,12 +4247,12 @@
@Override
public void appTransitionCancelled() {
- EventBus.getDefault().send(new AppTransitionFinishedEvent());
+ getComponent(Divider.class).onAppTransitionFinished();
}
@Override
public void appTransitionFinished() {
- EventBus.getDefault().send(new AppTransitionFinishedEvent());
+ getComponent(Divider.class).onAppTransitionFinished();
}
@Override
@@ -4424,9 +4426,9 @@
}
}
- public void fireNotificationHeadsUp() {
+ public void fireNotificationPulse() {
for (Callback callback : mCallbacks) {
- callback.onNotificationHeadsUp();
+ callback.onNotificationAlerted();
}
}
@@ -4462,7 +4464,7 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (mHeadsUpManager.hasNotifications()) {
+ if (mAmbientPulseManager.hasNotifications()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
setPulsing(true);
@@ -4543,7 +4545,11 @@
@Override
public void extendPulse() {
- mDozeScrimController.extendPulse();
+ if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
+ mAmbientPulseManager.extendPulse();
+ } else {
+ mDozeScrimController.extendPulse();
+ }
}
@Override
@@ -4629,6 +4635,8 @@
// for heads up notifications
protected HeadsUpManagerPhone mHeadsUpManager;
+ protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
@@ -4656,6 +4664,7 @@
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
+ private IDreamManager mDreamManager;
protected Display mDisplay;
@@ -4728,13 +4737,14 @@
: notification.fullScreenIntent;
final String notificationKey = sbn.getKey();
- final boolean afterKeyguardGone = intent.isActivity()
+ boolean isActivityIntent = intent.isActivity();
+ final boolean afterKeyguardGone = isActivityIntent
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
final boolean wasOccluded = mIsOccluded;
dismissKeyguardThenExecute(() -> {
// TODO: Some of this code may be able to move to NotificationEntryManager.
- if (mHeadsUpManager != null && mHeadsUpManager.contains(notificationKey)) {
+ if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
@@ -4769,7 +4779,7 @@
// 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 (intent.isActivity()) {
+ if (isActivityIntent) {
final int userId = intent.getCreatorUserHandle().getIdentifier();
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
&& mKeyguardManager.isDeviceLocked(userId)) {
@@ -4805,7 +4815,7 @@
}
launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, getActivityOptions(adapter));
- mActivityLaunchAnimator.setLaunchResult(launchResult);
+ mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
} catch (RemoteException | PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -4813,7 +4823,7 @@
// TODO: Dismiss Keyguard.
}
- if (intent.isActivity()) {
+ if (isActivityIntent) {
mAssistManager.hideAssist();
}
}
@@ -4932,7 +4942,7 @@
.startActivities(getActivityOptions(
mActivityLaunchAnimator.getLaunchAnimation(row, mIsOccluded)),
new UserHandle(UserHandle.getUserId(appUid)));
- mActivityLaunchAnimator.setLaunchResult(launchResult);
+ mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
if (shouldCollapse()) {
// Putting it back on the main thread, since we're touching views
mStatusBarWindow.post(() -> animateCollapsePanels(
@@ -4959,7 +4969,13 @@
}
void awakenDreams() {
- SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
}
@Override
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 e8389af..3db1456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -460,6 +460,11 @@
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
+ // hide() will happen asynchronously and might arrive after the scrims
+ // were already hidden, this means that the transition callback won't
+ // be triggered anymore and StatusBarWindowController will be forever in
+ // the fadingAway state.
+ mStatusBar.updateScrimController();
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index 6d75cfc..a6146a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -22,7 +22,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -71,7 +71,7 @@
@Override
public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) {
- return withPlugin(cls, PluginManager.getAction(cls));
+ return withPlugin(cls, PluginManager.Helper.getAction(cls));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index e9efaa1..0a72c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -17,10 +17,7 @@
package com.android.systemui.statusbar.policy;
import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.UserManager;
@@ -38,14 +35,13 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private final WifiStateReceiver mWifiStateReceiver = new WifiStateReceiver();
private final ConnectivityManager mConnectivityManager;
private final WifiManager mWifiManager;
private final Context mContext;
private int mHotspotState;
private int mNumConnectedDevices;
- private boolean mWaitingForCallback;
+ private boolean mWaitingForTerminalState;
public HotspotControllerImpl(Context context) {
mContext = context;
@@ -63,7 +59,9 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("HotspotController state:");
- pw.print(" mHotspotEnabled="); pw.println(stateToString(mHotspotState));
+ pw.print(" mHotspotState="); pw.println(stateToString(mHotspotState));
+ pw.print(" mNumConnectedDevices="); pw.println(mNumConnectedDevices);
+ pw.print(" mWaitingForTerminalState="); pw.println(mWaitingForTerminalState);
}
private static String stateToString(int hotspotState) {
@@ -99,7 +97,6 @@
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
synchronized (mCallbacks) {
mCallbacks.remove(callback);
-
updateWifiStateListeners(!mCallbacks.isEmpty());
}
}
@@ -112,7 +109,6 @@
* @param shouldListen whether we should start listening to various wifi statuses
*/
private void updateWifiStateListeners(boolean shouldListen) {
- mWifiStateReceiver.setListening(shouldListen);
if (shouldListen) {
mWifiManager.registerSoftApCallback(
this,
@@ -129,21 +125,27 @@
@Override
public boolean isHotspotTransient() {
- return mWaitingForCallback || (mHotspotState == WifiManager.WIFI_AP_STATE_ENABLING);
+ return mWaitingForTerminalState || (mHotspotState == WifiManager.WIFI_AP_STATE_ENABLING);
}
@Override
public void setHotspotEnabled(boolean enabled) {
- if (mWaitingForCallback) {
- if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for callback.");
+ if (mWaitingForTerminalState) {
+ if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for terminal state.");
return;
}
if (enabled) {
- OnStartTetheringCallback callback = new OnStartTetheringCallback();
- mWaitingForCallback = true;
+ mWaitingForTerminalState = true;
if (DEBUG) Log.d(TAG, "Starting tethering");
- mConnectivityManager.startTethering(
- ConnectivityManager.TETHERING_WIFI, false, callback);
+ mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false,
+ new ConnectivityManager.OnStartTetheringCallback() {
+ @Override
+ public void onTetheringFailed() {
+ if (DEBUG) Log.d(TAG, "onTetheringFailed");
+ maybeResetSoftApState();
+ fireHotspotChangedCallback();
+ }
+ });
} else {
mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
}
@@ -155,99 +157,56 @@
}
/**
- * Sends a hotspot changed callback with the new enabled status. Wraps
- * {@link #fireHotspotChangedCallback(boolean, int)} and assumes that the number of devices has
- * not changed.
- *
- * @param enabled whether the hotspot is enabled
+ * Sends a hotspot changed callback.
+ * Be careful when calling over multiple threads, especially if one of them is the main thread
+ * (as it can be blocked).
*/
- private void fireHotspotChangedCallback(boolean enabled) {
- fireHotspotChangedCallback(enabled, mNumConnectedDevices);
- }
-
- /**
- * Sends a hotspot changed callback with the new enabled status & the number of devices
- * connected to the hotspot. Be careful when calling over multiple threads, especially if one of
- * them is the main thread (as it can be blocked).
- *
- * @param enabled whether the hotspot is enabled
- * @param numConnectedDevices number of devices connected to the hotspot
- */
- private void fireHotspotChangedCallback(boolean enabled, int numConnectedDevices) {
+ private void fireHotspotChangedCallback() {
synchronized (mCallbacks) {
for (Callback callback : mCallbacks) {
- callback.onHotspotChanged(enabled, numConnectedDevices);
+ callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
}
}
}
@Override
public void onStateChanged(int state, int failureReason) {
- // Do nothing - we don't care about changing anything here.
+ // Update internal hotspot state for tracking before using any enabled/callback methods.
+ mHotspotState = state;
+
+ maybeResetSoftApState();
+ if (!isHotspotEnabled()) {
+ // Reset num devices if the hotspot is no longer enabled so we don't get ghost
+ // counters.
+ mNumConnectedDevices = 0;
+ }
+
+ fireHotspotChangedCallback();
+ }
+
+ private void maybeResetSoftApState() {
+ if (!mWaitingForTerminalState) {
+ return; // Only reset soft AP state if enabled from this controller.
+ }
+ switch (mHotspotState) {
+ case WifiManager.WIFI_AP_STATE_FAILED:
+ // TODO(b/110697252): must be called to reset soft ap state after failure
+ mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+ // Fall through
+ case WifiManager.WIFI_AP_STATE_ENABLED:
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ mWaitingForTerminalState = false;
+ break;
+ case WifiManager.WIFI_AP_STATE_ENABLING:
+ case WifiManager.WIFI_AP_STATE_DISABLING:
+ default:
+ break;
+ }
}
@Override
public void onNumClientsChanged(int numConnectedDevices) {
mNumConnectedDevices = numConnectedDevices;
- fireHotspotChangedCallback(isHotspotEnabled(), numConnectedDevices);
- }
-
- private final class OnStartTetheringCallback extends
- ConnectivityManager.OnStartTetheringCallback {
- @Override
- public void onTetheringStarted() {
- if (DEBUG) Log.d(TAG, "onTetheringStarted");
- mWaitingForCallback = false;
- // Don't fire a callback here, instead wait for the next update from wifi.
- }
-
- @Override
- public void onTetheringFailed() {
- if (DEBUG) Log.d(TAG, "onTetheringFailed");
- mWaitingForCallback = false;
- // TODO(b/110697252): stopTethering must be called to reset soft ap state after failure
- mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
- fireHotspotChangedCallback(isHotspotEnabled());
- // TODO: Show error.
- }
- }
-
- /**
- * Class to listen in on wifi state and update the hotspot state
- */
- private final class WifiStateReceiver extends BroadcastReceiver {
- private boolean mRegistered;
-
- public void setListening(boolean listening) {
- if (listening && !mRegistered) {
- if (DEBUG) Log.d(TAG, "Registering receiver");
- final IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- mContext.registerReceiver(this, filter);
- mRegistered = true;
- } else if (!listening && mRegistered) {
- if (DEBUG) Log.d(TAG, "Unregistering receiver");
- mContext.unregisterReceiver(this);
- mRegistered = false;
- }
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- int state = intent.getIntExtra(
- WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
- if (DEBUG) Log.d(TAG, "onReceive " + state);
-
- // Update internal hotspot state for tracking before using any enabled/callback methods.
- mHotspotState = state;
-
- if (!isHotspotEnabled()) {
- // Reset num devices if the hotspot is no longer enabled so we don't get ghost
- // counters.
- mNumConnectedDevices = 0;
- }
-
- fireHotspotChangedCallback(isHotspotEnabled());
- }
+ fireHotspotChangedCallback();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 945d9b9..2340786 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -19,6 +19,7 @@
import android.animation.ArgbEvaluator;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -35,6 +36,7 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
+import android.view.ContextThemeWrapper;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -388,6 +390,23 @@
}
}
+ /**
+ * Creates a KeyButtonDrawable with a shadow given its icon. The tint applied to the drawable
+ * is determined by the dark and light theme given by the context.
+ * @param ctx Context to get the drawable and determine the dark and light theme
+ * @param icon the icon resource id
+ * @param hasShadow if a shadow will appear with the drawable
+ * @return KeyButtonDrawable
+ */
+ public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
+ boolean hasShadow) {
+ final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
+ final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
+ Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
+ Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
+ return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow);
+ }
+
public static KeyButtonDrawable create(Context lightContext, Context darkContext,
@DrawableRes int iconResId, boolean hasShadow) {
return create(lightContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index cf39404..24a28cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -668,6 +668,7 @@
Locale current = mContext.getResources().getConfiguration().locale;
if (!current.equals(mLocale)) {
mLocale = current;
+ mWifiSignalController.refreshLocale();
notifyAllListeners();
}
}
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 52b813f..7dd0d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -586,7 +586,8 @@
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
if (mShowImeOnInputConnection && inputConnection != null) {
- final InputMethodManager imm = InputMethodManager.getInstance();
+ final InputMethodManager imm =
+ getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0adb439..29a6b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -403,7 +403,7 @@
boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
return new Pair<Integer, Boolean>(userId[0], hasCACerts);
} catch (RemoteException | InterruptedException | AssertionError e) {
- Log.i(TAG, e.getMessage());
+ Log.i(TAG, "failed to get CA certs", e);
new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
() -> new CACertLoader().execute(userId[0]),
CA_CERT_LOADING_RETRY_TIME_IN_MS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 0233ad1..693df88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -73,6 +73,10 @@
return new WifiState();
}
+ void refreshLocale() {
+ mWifiTracker.refreshLocale();
+ }
+
@Override
public void notifyListeners(SignalCallback callback) {
// only show wifi in the cluster if connected or if wifi-only
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index c294806..71414a2 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -34,11 +34,10 @@
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.plugins.PluginInstanceManager;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginPrefs;
+import com.android.systemui.shared.plugins.PluginInstanceManager;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginPrefs;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 088630f..5aa3035 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -32,7 +32,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
-import com.android.systemui.plugins.PluginPrefs;
+import com.android.systemui.shared.plugins.PluginPrefs;
public class TunerFragment extends PreferenceFragment {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 13c43f7..4810b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -34,6 +35,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.ContentResolver;
@@ -129,6 +131,7 @@
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
private final KeyguardManager mKeyguard;
+ private final ActivityManager mActivityManager;
private final AccessibilityManagerWrapper mAccessibilityMgr;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
@@ -154,6 +157,7 @@
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mController = Dependency.get(VolumeDialogController.class);
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mShowActiveStreamOnly = showActiveStreamOnly();
@@ -431,7 +435,9 @@
public void initSettingsH() {
if (mSettingsView != null) {
mSettingsView.setVisibility(
- mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE);
+ mDeviceProvisionedController.isCurrentUserSetup() &&
+ mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE ?
+ VISIBLE : GONE);
}
if (mSettingsIcon != null) {
mSettingsIcon.setOnClickListener(v -> {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e6e4857..62ca3f3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -40,7 +40,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
new file mode 100644
index 0000000..cfe9818
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
@@ -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 com.android.keyguard
+
+import android.support.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPatternViewTest : SysuiTestCase() {
+
+ private lateinit var mKeyguardPatternView: KeyguardPatternView
+ private lateinit var mSecurityMessage: KeyguardMessageArea
+
+ @Before
+ fun setup() {
+ val inflater = LayoutInflater.from(context)
+ mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null)
+ as KeyguardPatternView
+ mSecurityMessage = KeyguardMessageArea.findSecurityMessageDisplay(mKeyguardPatternView)
+ as KeyguardMessageArea
+ }
+
+ @Test
+ fun onResume_clearsTextField() {
+ mSecurityMessage.setMessage("an old message")
+ mKeyguardPatternView.onResume(KeyguardSecurityView.SCREEN_ON)
+ assertThat(mSecurityMessage.text).isEqualTo("")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index eaa0dcf..1cf73b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import android.content.Intent;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -71,7 +72,8 @@
mSensor = mSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
mSensor.getSensor(), mHostFake, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY);
+ DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
+ true /* debuggable */);
}
@Test
@@ -93,6 +95,19 @@
}
@Test
+ public void testAod_usesDebugValue() throws Exception {
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+
+ Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
+ intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1);
+ mScreen.onReceive(mContext, intent);
+ mSensor.sendSensorEvent(3);
+
+ assertEquals(1, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void testAod_usesLightSensorRespectingUserSetting() throws Exception {
int maxBrightness = 3;
Settings.System.putIntForUser(mContext.getContentResolver(),
@@ -160,7 +175,8 @@
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
null /* sensor */, mHostFake, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY);
+ DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
+ true /* debuggable */);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
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 c2da7f5..5c8336c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -88,14 +88,14 @@
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
- mHost.callback.onNotificationHeadsUp();
+ mHost.callback.onNotificationAlerted();
mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mHost.callback.onNotificationHeadsUp();
+ mHost.callback.onNotificationAlerted();
mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index f8aa28d..199c4c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -40,9 +40,9 @@
import android.support.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,7 +65,7 @@
private @Mock IActivityTaskManager mIActivityTaskManager;
private WorkLockActivityController mController;
- private SysUiTaskStackChangeListener mTaskStackListener;
+ private TaskStackChangeListener mTaskStackListener;
@Before
public void setUp() throws Exception {
@@ -75,8 +75,8 @@
doReturn("com.example.test").when(mContext).getPackageName();
// Construct controller. Save the TaskStackListener for injecting events.
- final ArgumentCaptor<SysUiTaskStackChangeListener> listenerCaptor =
- ArgumentCaptor.forClass(SysUiTaskStackChangeListener.class);
+ final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
+ ArgumentCaptor.forClass(TaskStackChangeListener.class);
mController = new WorkLockActivityController(mContext, mActivityManager,
mIActivityTaskManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 85cdfcc..12a122a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -14,8 +14,12 @@
package com.android.systemui.qs;
+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.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -23,15 +27,21 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Collections;
@@ -41,19 +51,37 @@
public class QSPanelTest extends SysuiTestCase {
private MetricsLogger mMetricsLogger;
+ private TestableLooper mTestableLooper;
private QSPanel mQsPanel;
+ @Mock
private QSTileHost mHost;
+ @Mock
private QSCustomizer mCustomizer;
+ @Mock
+ private QSTile dndTile;
+ private ViewGroup mParentView;
+ @Mock
+ private QSDetail.Callback mCallback;
@Before
public void setup() throws Exception {
- TestableLooper.get(this).runWithLooper(() -> {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+ mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null);
- mHost = mock(QSTileHost.class);
+ // Provides a parent with non-zero size for QSPanel
+ mParentView = new FrameLayout(mContext);
+ mParentView.addView(mQsPanel);
+
+ when(dndTile.getTileSpec()).thenReturn("dnd");
when(mHost.getTiles()).thenReturn(Collections.emptyList());
- mCustomizer = mock(QSCustomizer.class);
+ when(mHost.createTileView(any(), anyBoolean())).thenReturn(mock(QSTileView.class));
+
mQsPanel.setHost(mHost, mCustomizer);
+ mQsPanel.addTile(dndTile, true);
+ mQsPanel.setCallback(mCallback);
});
}
@@ -64,4 +92,31 @@
mQsPanel.setExpanded(false);
verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
}
+
+ @Test
+ public void testOpenDetailsWithExistingTile_NoException() {
+ mTestableLooper.processAllMessages();
+ mQsPanel.openDetails("dnd");
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
+ }
+
+/* @Test
+ public void testOpenDetailsWithNullParameter_NoException() {
+ mTestableLooper.processAllMessages();
+ mQsPanel.openDetails(null);
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
+ }*/
+
+ @Test
+ public void testOpenDetailsWithNonExistingTile_NoException() {
+ mTestableLooper.processAllMessages();
+ mQsPanel.openDetails("invalid-name");
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 04441ab..6d1ff8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -27,8 +27,10 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import com.android.systemui.plugins.annotations.Requires;
import org.junit.After;
@@ -64,6 +66,7 @@
@RunWith(AndroidJUnit4.class)
public class PluginInstanceManagerTest extends SysuiTestCase {
+ private static final String WHITELISTED_PACKAGE = "com.android.systemui";
// Static since the plugin needs to be generated by the PluginInstanceManager using newInstance.
private static Plugin sMockPlugin;
@@ -88,7 +91,7 @@
mMockVersionInfo = mock(VersionInfo.class);
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
- mMockManager, true);
+ mMockManager, true, new String[0]);
sMockPlugin = mock(Plugin.class);
when(sMockPlugin.getVersion()).thenReturn(1);
}
@@ -186,7 +189,7 @@
// Create a version that thinks the build is not debuggable.
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
- mMockManager, false);
+ mMockManager, false, new String[0]);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
@@ -199,6 +202,25 @@
}
@Test
+ public void testNonDebuggable_whitelist() throws Exception {
+ // Create a version that thinks the build is not debuggable.
+ mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
+ mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
+ mMockManager, false, new String[] {WHITELISTED_PACKAGE});
+ setupFakePmQuery();
+
+ mPluginInstanceManager.loadAll();
+
+ waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+ waitForIdleSync(mPluginInstanceManager.mMainHandler);
+
+ // Verify startup lifecycle
+ verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+ ArgumentCaptor.forClass(Context.class).capture());
+ verify(mMockListener).onPluginConnected(any(), any());
+ }
+
+ @Test
public void testCheckAndDisable() throws Exception {
createPlugin(); // Get into valid created state.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 94dbc2a..3c70205 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -11,10 +11,11 @@
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -26,7 +27,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -36,11 +36,13 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,6 +56,8 @@
@RunWithLooper
public class PluginManagerTest extends SysuiTestCase {
+ private static final String WHITELISTED_PACKAGE = "com.android.systemui";
+
private PluginInstanceManagerFactory mMockFactory;
private PluginInstanceManager mMockPluginInstance;
private PluginManagerImpl mPluginManager;
@@ -74,8 +78,14 @@
when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
.thenReturn(mMockPluginInstance);
+
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
- mMockExceptionHandler);
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+ });
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
}
@@ -87,7 +97,7 @@
when(mMockPluginInstance.getPlugin()).thenReturn(new PluginInfo(null, null, mockPlugin,
null, null));
Plugin result = mPluginManager.getOneShotPlugin("myAction", TestPlugin.class);
- assertTrue(result == mockPlugin);
+ assertSame(mockPlugin, result);
}
@Test
@@ -106,16 +116,37 @@
}
@Test
- public void testNonDebuggable() {
+ @RunWithLooper(setAsMainLooper = true)
+ public void testNonDebuggable_noWhitelist() {
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- mMockExceptionHandler);
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+ });
resetExceptionHandler();
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- verify(mMockPluginInstance, Mockito.never()).loadAll();
-
assertNull(mPluginManager.getOneShotPlugin("myPlugin", TestPlugin.class));
- verify(mMockPluginInstance, Mockito.never()).getPlugin();
+ assertNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
+ }
+
+ @Test
+ @RunWithLooper(setAsMainLooper = true)
+ public void testNonDebuggable_whitelistedPkg() {
+ mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[] {WHITELISTED_PACKAGE};
+ }
+ });
+ resetExceptionHandler();
+
+ mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
+ assertNotNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
+ assertNull(mPluginManager.getClassLoader("myPlugin", "com.android.invalidpackage"));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
index 0b4d9b5..9bad78d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -20,7 +20,8 @@
import android.support.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import com.android.systemui.plugins.annotations.Requires;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.DetailAdapter;
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 f21ce27..8b41516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -56,19 +56,19 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
- private static final int TEST_MINIMUM_DISPLAY_TIME = 200;
- private static final int TEST_AUTO_DISMISS_TIME = 500;
+ protected static final int TEST_MINIMUM_DISPLAY_TIME = 200;
+ protected static final int TEST_AUTO_DISMISS_TIME = 500;
// Number of notifications to use in tests requiring multiple notifications
private static final int TEST_NUM_NOTIFICATIONS = 4;
- private static final int TEST_TIMEOUT_TIME = 10000;
- private final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
+ protected static final int TEST_TIMEOUT_TIME = 10000;
+ protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
private AlertingNotificationManager mAlertingNotificationManager;
protected NotificationData.Entry mEntry;
protected Handler mTestHandler;
private StatusBarNotification mSbn;
- private boolean mTimedOut = false;
+ protected boolean mTimedOut = false;
@Mock protected ExpandableNotificationRow mRow;
@@ -122,7 +122,7 @@
public void testShowNotification_addsEntry() {
mAlertingNotificationManager.showNotification(mEntry);
- assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
assertTrue(mAlertingNotificationManager.hasNotifications());
assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.key));
}
@@ -136,7 +136,7 @@
TestableLooper.get(this).processMessages(1);
assertFalse("Test timed out", mTimedOut);
- assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -146,7 +146,7 @@
// Try to remove but defer, since the notification has not been shown long enough.
mAlertingNotificationManager.removeNotification(mEntry.key, false /* releaseImmediately */);
- assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -156,7 +156,7 @@
// Remove forcibly with releaseImmediately = true.
mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */);
- assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -174,10 +174,18 @@
}
@Test
- public void testShouldExtendLifetime_notShownLongEnough() {
+ public void testCanRemoveImmediately_notShownLongEnough() {
mAlertingNotificationManager.showNotification(mEntry);
- // The entry has just been added so the lifetime should be extended
+ // The entry has just been added so we should not remove immediately.
+ assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.key));
+ }
+
+ @Test
+ public void testShouldExtendLifetime() {
+ mAlertingNotificationManager.showNotification(mEntry);
+
+ // While the entry is alerting, it should not be removable.
assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
new file mode 100644
index 0000000..f0344e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AmbientPulseManagerTest extends AlertingNotificationManagerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ private static final int TEST_EXTENSION_TIME = 500;
+ private AmbientPulseManager mAmbientPulseManager;
+ private boolean mLivesPastNormalTime;
+
+ protected AlertingNotificationManager createAlertingNotificationManager() {
+ return mAmbientPulseManager;
+ }
+
+ @Before
+ public void setUp() {
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mAmbientPulseManager.mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
+ mAmbientPulseManager.mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+ mAmbientPulseManager.mExtensionTime = TEST_EXTENSION_TIME;
+ super.setUp();
+ mAmbientPulseManager.mHandler = mTestHandler;
+ }
+
+ @Test
+ public void testExtendPulse() {
+ mAmbientPulseManager.showNotification(mEntry);
+ Runnable pastNormalTimeRunnable =
+ () -> mLivesPastNormalTime = mAmbientPulseManager.isAlerting(mEntry.key);
+ mTestHandler.postDelayed(pastNormalTimeRunnable,
+ mAmbientPulseManager.mAutoDismissNotificationDecay +
+ mAmbientPulseManager.mExtensionTime / 2);
+ mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
+
+ mAmbientPulseManager.extendPulse();
+
+ // Wait for normal time runnable and extended remove runnable and process them on arrival.
+ TestableLooper.get(this).processMessages(2);
+
+ assertFalse("Test timed out", mTimedOut);
+ assertTrue("Pulse was not extended", mLivesPastNormalTime);
+ assertFalse(mAmbientPulseManager.isAlerting(mEntry.key));
+ }
+}
+
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 09c1931..da59450 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -79,13 +79,14 @@
Dependency.get(NotificationLockscreenUserManager.class);
NotificationViewHierarchyManager viewHierarchyManager =
Dependency.get(NotificationViewHierarchyManager.class);
+ NotificationGroupManager groupManager = Dependency.get(NotificationGroupManager.class);
when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(lockscreenUserManager);
- when(mPresenter.getGroupManager()).thenReturn(
- Dependency.get(NotificationGroupManager.class));
+ when(mPresenter.getGroupManager()).thenReturn(groupManager);
entryManager.setUpWithPresenter(mPresenter, mListContainer, mEntryManagerCallback,
mHeadsUpManager);
+ groupManager.setHeadsUpManager(mHeadsUpManager);
gutsManager.setUpWithPresenter(mPresenter, mListContainer, mCheckSaveListener,
mOnClickListener);
notificationLogger.setUpWithEntryManager(entryManager, mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index a7758a6..515c109 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -40,8 +40,11 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.google.android.collect.Lists;
@@ -61,7 +64,9 @@
// Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotificationData mNotificationData;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock private StatusBarKeyguardViewManager mKeyguardViewManager;
private int mCurrentUserId;
private TestNotificationLockscreenUserManager mLockscreenUserManager;
@@ -79,9 +84,11 @@
when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
+ when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
mLockscreenUserManager.setUpWithPresenter(mPresenter, mEntryManager);
+ mLockscreenUserManager.setKeyguardViewManager(mKeyguardViewManager);
}
@Test
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 b2170fa..edf29ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,6 +31,7 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -63,6 +64,7 @@
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
public ExpandableNotificationRow createRow() throws Exception {
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
new file mode 100644
index 0000000..435ede4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.RemoteAnimationAdapter;
+import android.view.View;
+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.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ActivityLaunchAnimatorTest extends SysuiTestCase {
+
+ private ActivityLaunchAnimator mLaunchAnimator;
+ private ActivityLaunchAnimator.Callback mCallback = mock(ActivityLaunchAnimator.Callback.class);
+ private StatusBarWindowView mStatusBarWindowView = mock(StatusBarWindowView.class);
+ private NotificationListContainer mNotificationContainer
+ = mock(NotificationListContainer.class);
+ private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
+
+ @Before
+ public void setUp() throws Exception {
+ when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
+ mLaunchAnimator = new ActivityLaunchAnimator(
+ mStatusBarWindowView,
+ mCallback,
+ mock(NotificationPanelView.class),
+ mNotificationContainer);
+
+ }
+
+ @Test
+ public void testReturnsNullIfNotEnabled() {
+ when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
+ RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
+ false /* occluded */);
+ Assert.assertTrue("The LaunchAnimator generated an animation even though animations are "
+ + "disabled", launchAnimation == null);
+ }
+
+ @Test
+ public void testNotWorkingWhenOccluded() {
+ when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
+ RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
+ true /* occluded */);
+ Assert.assertTrue("The LaunchAnimator generated an animation even though we're occluded",
+ launchAnimation == null);
+ }
+
+ @Test
+ public void testTimeoutCalled() {
+ RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
+ false /* occluded */);
+ Assert.assertTrue("No animation generated", launchAnimation != null);
+ executePostsImmediately(mStatusBarWindowView);
+ mLaunchAnimator.setLaunchResult(ActivityManager.START_SUCCESS,
+ true /* wasIntentActivity */);
+ verify(mCallback).onExpandAnimationTimedOut();
+ }
+
+ private void executePostsImmediately(View view) {
+ doAnswer((i) -> {
+ Runnable run = i.getArgument(0);
+ run.run();
+ return null;
+ }).when(view).post(any());
+ doAnswer((i) -> {
+ Runnable run = i.getArgument(0);
+ run.run();
+ return null;
+ }).when(view).postDelayed(any(), anyLong());
+ }
+}
+
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 c236fbe..ca968a8 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
@@ -158,6 +158,11 @@
PollingCheck.waitFor(1000,
() -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility());
}
+ private void ensureNoUndoButton() {
+ PollingCheck.waitFor(1000,
+ () -> GONE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()
+ && !mNotificationInfo.isAnimating());
+ }
private void waitForStopButton() {
PollingCheck.waitFor(1000,
() -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
@@ -583,9 +588,6 @@
true /* isUserSentimentNegative */);
mNotificationInfo.findViewById(R.id.block).performClick();
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
-
mTestableLooper.processAllMessages();
verify(listener).checkSave(any(Runnable.class), eq(mSbn));
}
@@ -805,7 +807,7 @@
}
@Test
- public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception {
+ public void testBlockDoesNothingIfCheckSaveListenerIsNoOp() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
@@ -813,10 +815,10 @@
}, null, null, true, true);
mNotificationInfo.findViewById(R.id.block).performClick();
- waitForUndoButton();
+ mTestableLooper.processAllMessages();
+ ensureNoUndoButton();
mNotificationInfo.handleCloseControls(true, false);
- mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
}
@@ -831,6 +833,10 @@
}, null, null, true, false);
mNotificationInfo.findViewById(R.id.block).performClick();
+ mTestableLooper.processAllMessages();
+ verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
+
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
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 06265e5..18dd1fd 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
@@ -15,10 +15,15 @@
package com.android.systemui.statusbar.notification.row;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
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 static org.mockito.Mockito.doNothing;
import android.app.Notification;
import android.service.notification.StatusBarNotification;
@@ -27,7 +32,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
-import android.testing.ViewUtils;
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -36,6 +40,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -72,6 +77,7 @@
row.resetMenu();
}
+
@Test
public void testNoAppOpsInSlowSwipe() {
NotificationMenuRow row = new NotificationMenuRow(mContext);
@@ -86,4 +92,255 @@
// one for snooze and one for noti blocking
assertEquals(2, container.getChildCount());
}
+
+ @Test
+ public void testIsSnappedAndOnSameSide() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuSnapped()).thenReturn(true);
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.isMenuSnappedOnLeft()).thenReturn(true);
+
+ assertTrue("Showing on left and on left", row.isSnappedAndOnSameSide());
+
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.isMenuSnappedOnLeft()).thenReturn(false);
+ assertTrue("Snapped to right and on right", row.isSnappedAndOnSameSide());
+
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.isMenuSnapped()).thenReturn(false);
+ assertFalse("Snapped to right and on left", row.isSnappedAndOnSameSide());
+
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.isMenuSnappedOnLeft()).thenReturn(true);
+ when(row.isMenuVisible()).thenReturn(false);
+ assertFalse("Snapped to left and on left, but menu not visible",
+ row.isSnappedAndOnSameSide());
+
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuSnapped()).thenReturn(false);
+ assertFalse("Snapped to left and on left, but not actually snapped to",
+ row.isSnappedAndOnSameSide());
+ }
+
+ @Test
+ public void testGetMenuSnapTarget() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuOnLeft()).thenReturn(true);
+ doReturn(30).when(row).getSpaceForMenu();
+
+ assertEquals("When on left, snap target is space for menu",
+ 30, row.getMenuSnapTarget());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ assertEquals("When on right, snap target is negative space for menu",
+ -30, row.getMenuSnapTarget());
+ }
+
+ @Test
+ public void testIsSwipedEnoughToShowMenu() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuOnLeft()).thenReturn(true);
+ doReturn(40f).when(row).getMinimumSwipeDistance();
+
+ when(row.getTranslation()).thenReturn(30f);
+ assertFalse("on left, translation is less than min", row.isSwipedEnoughToShowMenu());
+
+ when(row.getTranslation()).thenReturn(50f);
+ assertTrue("on left, translation is greater than min", row.isSwipedEnoughToShowMenu());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(-30f);
+ assertFalse("on right, translation is greater than -min", row.isSwipedEnoughToShowMenu());
+
+ when(row.getTranslation()).thenReturn(-50f);
+ assertTrue("on right, translation is less than -min", row.isSwipedEnoughToShowMenu());
+
+ when(row.isMenuVisible()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(30f);
+ assertFalse("on left, translation greater than min, but not visible",
+ row.isSwipedEnoughToShowMenu());
+ }
+
+ @Test
+ public void testIsWithinSnapMenuThreshold() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doReturn(30f).when(row).getSnapBackThreshold();
+ doReturn(50f).when(row).getDismissThreshold();
+
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.getTranslation()).thenReturn(40f);
+ assertTrue("When on left, translation is between min and max",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(20f);
+ assertFalse("When on left, translation is less than min",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(60f);
+ assertFalse("When on left, translation is greater than max",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(-40f);
+ assertTrue("When on right, translation is between -min and -max",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(-20f);
+ assertFalse("When on right, translation is greater than -min",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(-60f);
+ assertFalse("When on right, translation is less than -max",
+ row.isWithinSnapMenuThreshold());
+ }
+
+ @Test
+ public void testShouldSnapBack() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doReturn(40f).when(row).getSnapBackThreshold();
+ when(row.isMenuVisible()).thenReturn(false);
+ when(row.isMenuOnLeft()).thenReturn(true);
+
+ when(row.getTranslation()).thenReturn(50f);
+ assertFalse("On left, translation greater than minimum target", row.shouldSnapBack());
+
+ when(row.getTranslation()).thenReturn(30f);
+ assertTrue("On left, translation less than minimum target", row.shouldSnapBack());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(-50f);
+ assertFalse("On right, translation less than minimum target", row.shouldSnapBack());
+
+ when(row.getTranslation()).thenReturn(-30f);
+ assertTrue("On right, translation greater than minimum target", row.shouldSnapBack());
+ }
+
+ @Test
+ public void testCanBeDismissed() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+
+ when(row.getParent()).thenReturn(parent);
+ when(parent.canViewBeDismissed()).thenReturn(true);
+
+ assertTrue("Row can be dismissed if parent can be dismissed", row.canBeDismissed());
+
+ when(parent.canViewBeDismissed()).thenReturn(false);
+ assertFalse("Row cannot be dismissed if parent cannot be dismissed",
+ row.canBeDismissed());
+ }
+
+ @Test
+ public void testIsTowardsMenu() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuOnLeft()).thenReturn(true);
+
+ assertTrue("menu on left, movement is negative", row.isTowardsMenu(-30f));
+ assertFalse("menu on left, movement is positive", row.isTowardsMenu(30f));
+ assertTrue("menu on left, movement is 0", row.isTowardsMenu(0f));
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ assertTrue("menu on right, movement is positive", row.isTowardsMenu(30f));
+ assertFalse("menu on right, movement is negative", row.isTowardsMenu(-30f));
+ assertTrue("menu on right, movement is 0", row.isTowardsMenu(0f));
+
+ when(row.isMenuVisible()).thenReturn(false);
+ assertFalse("menu on left, movement is negative, but menu not visible",
+ row.isTowardsMenu(-30f));
+ }
+
+ @Test
+ public void onSnapBack() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ NotificationMenuRowPlugin.OnMenuEventListener listener = mock(NotificationMenuRowPlugin
+ .OnMenuEventListener.class);
+ row.setMenuClickListener(listener);
+ ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+ when(row.getParent()).thenReturn(parent);
+ doNothing().when(row).cancelDrag();
+
+ row.onSnapOpen();
+
+ assertTrue("before onSnapClosed, row is snapped to", row.isMenuSnapped());
+ assertFalse("before onSnapClosed, row is not snapping", row.isSnapping());
+
+ row.onSnapClosed();
+
+ assertFalse("after onSnapClosed, row is not snapped to", row.isMenuSnapped());
+ assertTrue("after onSnapClosed, row is snapping", row.isSnapping());
+ }
+
+ @Test
+ public void testOnSnap() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuOnLeft()).thenReturn(true);
+ NotificationMenuRowPlugin.OnMenuEventListener listener = mock(NotificationMenuRowPlugin
+ .OnMenuEventListener.class);
+ row.setMenuClickListener(listener);
+ ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+ when(row.getParent()).thenReturn(parent);
+
+ assertFalse("before onSnapOpen, row is not snapped to", row.isMenuSnapped());
+ assertFalse("before onSnapOpen, row is not snapped on left", row.isMenuSnappedOnLeft());
+
+ row.onSnapOpen();
+
+ assertTrue("after onSnapOpen, row is snapped to", row.isMenuSnapped());
+ assertTrue("after onSnapOpen, row is snapped on left", row.isMenuSnapped());
+ verify(listener, times(1)).onMenuShown(parent);
+ }
+
+ @Test
+ public void testOnDismiss() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doNothing().when(row).cancelDrag();
+ row.onSnapOpen();
+
+ assertFalse("before onDismiss, row is not dismissing", row.isDismissing());
+ assertTrue("before onDismiss, row is showing", row.isMenuSnapped());
+
+ row.onDismiss();
+
+ verify(row, times(1)).cancelDrag();
+ assertTrue("after onDismiss, row is dismissing", row.isDismissing());
+ assertFalse("after onDismiss, row is not showing", row.isMenuSnapped());
+ }
+
+ @Test
+ public void testOnDown() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doNothing().when(row).beginDrag();
+
+ row.onTouchStart();
+
+ verify(row, times(1)).beginDrag();
+ }
+
+ @Test
+ public void testOnUp() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ row.onTouchStart();
+
+ assertTrue("before onTouchEnd, isUserTouching is true", row.isUserTouching());
+
+ row.onTouchEnd();
+
+ assertFalse("after onTouchEnd, isUserTouching is false", row.isUserTouching());
+ }
+
+ @Test
+ public void testIsMenuVisible() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ row.setMenuAlpha(0);
+
+ assertFalse("when alpha is 0, menu is not visible", row.isMenuVisible());
+
+ row.setMenuAlpha(0.5f);
+ assertTrue("when alpha is .5, menu is visible", row.isMenuVisible());
+ }
}
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 087aa59..2728453 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
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.stack;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
@@ -51,7 +50,7 @@
@Test
public void testGetMaxAllowedVisibleChildren_ambient() {
- mGroup.setShowAmbient(true);
+ mGroup.setOnAmbient(true);
Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
}
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 1e3d42b..b545e61 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
@@ -32,6 +32,7 @@
import android.os.IPowerManager;
import android.os.Looper;
import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -39,7 +40,6 @@
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -91,7 +91,7 @@
@Mock private NotificationData mNotificationData;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
- @Mock private SystemServicesProxy mSystemServicesProxy;
+ @Mock private IDreamManager mDreamManager;
private PowerManager mPowerManager;
private TestableNotificationEntryManager mEntryManager;
@@ -111,7 +111,7 @@
mPowerManager = new PowerManager(mContext, powerManagerService,
Handler.createAsync(Looper.myLooper()));
- mEntryManager = new TestableNotificationEntryManager(mSystemServicesProxy, mPowerManager,
+ mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
mContext);
mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, mHeadsUpManager,
mNotificationData);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
new file mode 100644
index 0000000..b5f67c06
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.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.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockitoSession;
+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 static org.mockito.Mockito.when;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.service.notification.StatusBarNotification;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.MotionEvent;
+
+import com.android.systemui.SwipeHelper;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationMenuRow;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for {@link NotificationSwipeHelper}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationSwipeHelperTest extends SysuiTestCase {
+
+ private NotificationSwipeHelper mSwipeHelper;
+ private NotificationSwipeHelper.NotificationCallback mCallback;
+ private NotificationMenuRowPlugin.OnMenuEventListener mListener;
+ private View mView;
+ private MotionEvent mEvent;
+ private NotificationMenuRowPlugin mMenuRow;
+ private Handler mHandler;
+ private ExpandableNotificationRow mNotificationRow;
+ private Runnable mFalsingCheck;
+
+ @Rule public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ @UiThreadTest
+ public void setUp() throws Exception {
+ mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
+ mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
+ mSwipeHelper = spy(new NotificationSwipeHelper(SwipeHelper.X, mCallback, mContext, mListener));
+ mView = mock(View.class);
+ mEvent = mock(MotionEvent.class);
+ mMenuRow = mock(NotificationMenuRowPlugin.class);
+ mNotificationRow = mock(ExpandableNotificationRow.class);
+ mHandler = mock(Handler.class);
+ mFalsingCheck = mock(Runnable.class);
+ }
+
+ @Test
+ public void testSetExposedMenuView() {
+ assertEquals("intialized with null exposed menu view", null,
+ mSwipeHelper.getExposedMenuView());
+ mSwipeHelper.setExposedMenuView(mView);
+ assertEquals("swipe helper has correct exposedMenuView after setExposedMenuView to a view",
+ mView, mSwipeHelper.getExposedMenuView());
+ mSwipeHelper.setExposedMenuView(null);
+ assertEquals("swipe helper has null exposedMenuView after setExposedMenuView to null",
+ null, mSwipeHelper.getExposedMenuView());
+ }
+
+ @Test
+ public void testClearExposedMenuView() {
+ doNothing().when(mSwipeHelper).setExposedMenuView(mView);
+ mSwipeHelper.clearExposedMenuView();
+ verify(mSwipeHelper, times(1)).setExposedMenuView(null);
+ }
+
+ @Test
+ public void testGetTranslatingParentView() {
+ assertEquals("intialized with null translating parent view", null,
+ mSwipeHelper.getTranslatingParentView());
+ mSwipeHelper.setTranslatingParentView(mView);
+ assertEquals("has translating parent view after setTranslatingParentView with a view",
+ mView, mSwipeHelper.getTranslatingParentView());
+ }
+
+ @Test
+ public void testClearTranslatingParentView() {
+ doNothing().when(mSwipeHelper).setTranslatingParentView(null);
+ mSwipeHelper.clearTranslatingParentView();
+ verify(mSwipeHelper, times(1)).setTranslatingParentView(null);
+ }
+
+ @Test
+ public void testSetCurrentMenuRow() {
+ assertEquals("currentMenuRow initializes to null", null,
+ mSwipeHelper.getCurrentMenuRow());
+ mSwipeHelper.setCurrentMenuRow(mMenuRow);
+ assertEquals("currentMenuRow set correctly after setCurrentMenuRow", mMenuRow,
+ mSwipeHelper.getCurrentMenuRow());
+ mSwipeHelper.setCurrentMenuRow(null);
+ assertEquals("currentMenuRow set to null after setCurrentMenuRow to null",
+ null, mSwipeHelper.getCurrentMenuRow());
+ }
+
+ @Test
+ public void testClearCurrentMenuRow() {
+ doNothing().when(mSwipeHelper).setCurrentMenuRow(null);
+ mSwipeHelper.clearCurrentMenuRow();
+ verify(mSwipeHelper, times(1)).setCurrentMenuRow(null);
+ }
+
+ @Test
+ public void testOnDownUpdate_ExpandableNotificationRow() {
+ when(mSwipeHelper.getHandler()).thenReturn(mHandler);
+ when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck);
+ doNothing().when(mSwipeHelper).resetExposedMenuView(true, false);
+ doNothing().when(mSwipeHelper).clearCurrentMenuRow();
+ doNothing().when(mSwipeHelper).initializeRow(any());
+
+ mSwipeHelper.onDownUpdate(mNotificationRow, mEvent);
+
+ verify(mSwipeHelper, times(1)).clearCurrentMenuRow();
+ verify(mHandler, times(1)).removeCallbacks(mFalsingCheck);
+ verify(mSwipeHelper, times(1)).resetExposedMenuView(true, false);
+ verify(mSwipeHelper, times(1)).initializeRow(mNotificationRow);
+ }
+
+ @Test
+ public void testOnDownUpdate_notExpandableNotificationRow() {
+ when(mSwipeHelper.getHandler()).thenReturn(mHandler);
+ when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck);
+ doNothing().when(mSwipeHelper).resetExposedMenuView(true, false);
+ doNothing().when(mSwipeHelper).clearCurrentMenuRow();
+ doNothing().when(mSwipeHelper).initializeRow(any());
+
+ mSwipeHelper.onDownUpdate(mView, mEvent);
+
+ verify(mSwipeHelper, times(1)).clearCurrentMenuRow();
+ verify(mHandler, times(1)).removeCallbacks(mFalsingCheck);
+ verify(mSwipeHelper, times(1)).resetExposedMenuView(true, false);
+ verify(mSwipeHelper, times(0)).initializeRow(any());
+ }
+
+ @Test
+ public void testOnMoveUpdate_menuRow() {
+ when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow);
+ when(mSwipeHelper.getHandler()).thenReturn(mHandler);
+ when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck);
+
+ mSwipeHelper.onMoveUpdate(mView, mEvent, 0, 10);
+
+ verify(mHandler, times(1)).removeCallbacks(mFalsingCheck);
+ verify(mMenuRow, times(1)).onTouchMove(10);
+ }
+
+ @Test
+ public void testOnMoveUpdate_noMenuRow() {
+ when(mSwipeHelper.getHandler()).thenReturn(mHandler);
+ when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck);
+
+ mSwipeHelper.onMoveUpdate(mView, mEvent, 0, 10);
+
+ verify(mHandler, times(1)).removeCallbacks(mFalsingCheck);
+ }
+
+ @Test
+ public void testHandleUpEvent_noMenuRow() {
+ assertFalse("Menu row does not exist",
+ mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0));
+ }
+
+ @Test
+ public void testHandleUpEvent_menuRow() {
+ when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow);
+ doNothing().when(mSwipeHelper).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow);
+
+ assertTrue("Menu row exists",
+ mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0));
+ verify(mMenuRow, times(1)).onTouchEnd();
+ verify(mSwipeHelper, times(1)).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow);
+ }
+
+ @Test
+ public void testDismissChild_notExpanded() {
+ when(mCallback.isExpanded()).thenReturn(false);
+ doNothing().when(mSwipeHelper).superDismissChild(mView, 0, false);
+ doNothing().when(mSwipeHelper).handleMenuCoveredOrDismissed();
+
+ mSwipeHelper.dismissChild(mView, 0, false);
+
+ verify(mSwipeHelper, times(1)).superDismissChild(mView, 0, false);
+ verify(mCallback, times(0)).handleChildViewDismissed(mView);
+ verify(mCallback, times(1)).onDismiss();
+ verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed();
+ }
+
+ @Test
+ public void testSnapchild_targetIsZero() {
+ doNothing().when(mSwipeHelper).superSnapChild(mView, 0, 0);
+ mSwipeHelper.snapChild(mView, 0, 0);
+
+ verify(mCallback, times(1)).onDragCancelled(mView);
+ verify(mSwipeHelper, times(1)).superSnapChild(mView, 0, 0);
+ verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed();
+ }
+
+
+ @Test
+ public void testSnapchild_targetNotZero() {
+ doNothing().when(mSwipeHelper).superSnapChild(mView, 10, 0);
+ mSwipeHelper.snapChild(mView, 10, 0);
+
+ verify(mCallback, times(1)).onDragCancelled(mView);
+ verify(mSwipeHelper, times(1)).superSnapChild(mView, 10, 0);
+ verify(mSwipeHelper, times(0)).handleMenuCoveredOrDismissed();
+ }
+
+ @Test
+ public void testSnooze() {
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ SnoozeOption snoozeOption = mock(SnoozeOption.class);
+ mSwipeHelper.snooze(sbn, snoozeOption);
+ verify(mCallback, times(1)).onSnooze(sbn, snoozeOption);
+ }
+
+ @Test
+ public void testGetViewTranslationAnimator_notExpandableNotificationRow() {
+ Animator animator = mock(Animator.class);
+ AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class);
+ doReturn(animator).when(mSwipeHelper).superGetViewTranslationAnimator(mView, 0, listener);
+
+ assertEquals("returns the correct animator from super", animator,
+ mSwipeHelper.getViewTranslationAnimator(mView, 0, listener));
+
+ verify(mSwipeHelper, times(1)).superGetViewTranslationAnimator(mView, 0, listener);
+ }
+
+ @Test
+ public void testGetViewTranslationAnimator_expandableNotificationRow() {
+ Animator animator = mock(Animator.class);
+ AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class);
+ doReturn(animator).when(mNotificationRow).getTranslateViewAnimator(0, listener);
+
+ assertEquals("returns the correct animator from super when view is an ENR", animator,
+ mSwipeHelper.getViewTranslationAnimator(mNotificationRow, 0, listener));
+
+ verify(mNotificationRow, times(1)).getTranslateViewAnimator(0, listener);
+ }
+
+ @Test
+ public void testSetTranslation() {
+ mSwipeHelper.setTranslation(mNotificationRow, 0);
+ verify(mNotificationRow, times(1)).setTranslation(0);
+ }
+
+ @Test
+ public void testGetTranslation() {
+ doReturn(30f).when(mNotificationRow).getTranslation();
+
+ assertEquals("Returns getTranslation for the ENR",
+ mSwipeHelper.getTranslation(mNotificationRow), 30f);
+
+ verify(mNotificationRow, times(1)).getTranslation();
+ }
+
+ @Test
+ public void testDismiss() {
+ doNothing().when(mSwipeHelper).dismissChild(mView, 0, true);
+ doReturn(false).when(mSwipeHelper).swipedFastEnough();
+
+ mSwipeHelper.dismiss(mView, 0);
+
+ verify(mSwipeHelper, times(1)).swipedFastEnough();
+ verify(mSwipeHelper, times(1)).dismissChild(mView, 0, true);
+ }
+
+ @Test
+ public void testSnapOpen() {
+ doNothing().when(mSwipeHelper).snapChild(mView, 30, 0);
+
+ mSwipeHelper.snapOpen(mView, 30, 0);
+
+ verify(mSwipeHelper, times(1)).snapChild(mView, 30, 0);
+ }
+
+ @Test
+ public void testSnapClosed() {
+ doNothing().when(mSwipeHelper).snapChild(mView, 0, 0);
+
+ mSwipeHelper.snapClosed(mView, 0);
+
+ verify(mSwipeHelper, times(1)).snapChild(mView, 0, 0);
+ }
+
+ @Test
+ public void testGetMinDismissVelocity() {
+ doReturn(30f).when(mSwipeHelper).getEscapeVelocity();
+
+ assertEquals("Returns getEscapeVelocity", 30f, mSwipeHelper.getMinDismissVelocity());
+ }
+
+ @Test
+ public void onMenuShown_noAntiFalsing() {
+ doNothing().when(mSwipeHelper).setExposedMenuView(mView);
+ doReturn(mView).when(mSwipeHelper).getTranslatingParentView();
+ doReturn(mHandler).when(mSwipeHelper).getHandler();
+ doReturn(false).when(mCallback).isAntiFalsingNeeded();
+ doReturn(mFalsingCheck).when(mSwipeHelper).getFalsingCheck();
+
+ mSwipeHelper.onMenuShown(mView);
+
+ verify(mSwipeHelper, times(1)).setExposedMenuView(mView);
+ verify(mCallback, times(1)).onDragCancelled(mView);
+ verify(mCallback, times(1)).isAntiFalsingNeeded();
+
+ verify(mHandler, times(0)).removeCallbacks(mFalsingCheck);
+ verify(mHandler, times(0)).postDelayed(mFalsingCheck, mSwipeHelper.COVER_MENU_DELAY);
+ }
+
+ @Test
+ public void onMenuShown_antiFalsing() {
+ doNothing().when(mSwipeHelper).setExposedMenuView(mView);
+ doReturn(mView).when(mSwipeHelper).getTranslatingParentView();
+ doReturn(mHandler).when(mSwipeHelper).getHandler();
+ doReturn(true).when(mCallback).isAntiFalsingNeeded();
+ doReturn(mFalsingCheck).when(mSwipeHelper).getFalsingCheck();
+
+ mSwipeHelper.onMenuShown(mView);
+
+ verify(mSwipeHelper, times(1)).setExposedMenuView(mView);
+ verify(mCallback, times(1)).onDragCancelled(mView);
+ verify(mCallback, times(1)).isAntiFalsingNeeded();
+
+ verify(mHandler, times(1)).removeCallbacks(mFalsingCheck);
+ verify(mHandler, times(1)).postDelayed(mFalsingCheck, mSwipeHelper.COVER_MENU_DELAY);
+ }
+
+ @Test
+ public void testResetExposedMenuView_noReset() {
+ doReturn(false).when(mSwipeHelper).shouldResetMenu(false);
+ doNothing().when(mSwipeHelper).clearExposedMenuView();
+
+ mSwipeHelper.resetExposedMenuView(false, false);
+
+ verify(mSwipeHelper, times(1)).shouldResetMenu(false);
+
+ // should not clear exposed menu row
+ verify(mSwipeHelper, times(0)).clearExposedMenuView();
+ }
+
+ @Test
+ public void testResetExposedMenuView_animate() {
+ Animator animator = mock(Animator.class);
+
+ doReturn(true).when(mSwipeHelper).shouldResetMenu(false);
+ doReturn(mNotificationRow).when(mSwipeHelper).getExposedMenuView();
+ doReturn(false).when(mNotificationRow).isRemoved();
+ doReturn(animator).when(mSwipeHelper).getViewTranslationAnimator(mNotificationRow, 0, null);
+ doNothing().when(mSwipeHelper).clearExposedMenuView();
+
+ mSwipeHelper.resetExposedMenuView(true, false);
+
+ verify(mSwipeHelper, times(1)).shouldResetMenu(false);
+
+ // should retrieve and start animator
+ verify(mSwipeHelper, times(1)).getViewTranslationAnimator(mNotificationRow, 0, null);
+ verify(animator, times(1)).start();
+
+ // should not reset translation on row directly
+ verify(mNotificationRow, times(0)).resetTranslation();
+
+ // should clear exposed menu row
+ verify(mSwipeHelper, times(1)).clearExposedMenuView();
+ }
+
+
+ @Test
+ public void testResetExposedMenuView_noAnimate() {
+ Animator animator = mock(Animator.class);
+
+ doReturn(true).when(mSwipeHelper).shouldResetMenu(false);
+ doReturn(mNotificationRow).when(mSwipeHelper).getExposedMenuView();
+ doReturn(false).when(mNotificationRow).isRemoved();
+ doReturn(animator).when(mSwipeHelper).getViewTranslationAnimator(mNotificationRow, 0, null);
+ doNothing().when(mSwipeHelper).clearExposedMenuView();
+
+ mSwipeHelper.resetExposedMenuView(false, false);
+
+ verify(mSwipeHelper, times(1)).shouldResetMenu(false);
+
+ // should not retrieve and start animator
+ verify(mSwipeHelper, times(0)).getViewTranslationAnimator(mNotificationRow, 0, null);
+ verify(animator, times(0)).start();
+
+ // should reset translation on row directly
+ verify(mNotificationRow, times(1)).resetTranslation();
+
+ // should clear exposed menu row
+ verify(mSwipeHelper, times(1)).clearExposedMenuView();
+ }
+
+ @Test
+ public void testIsTouchInView() {
+ assertEquals("returns false when view is null", false,
+ NotificationSwipeHelper.isTouchInView(mEvent, null));
+
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
+
+ doReturn(20).when(mView).getWidth();
+ doReturn(20).when(mView).getHeight();
+
+ Answer answer = (Answer) invocation -> {
+ int[] arr = invocation.getArgument(0);
+ arr[0] = 0;
+ arr[1] = 0;
+ return null;
+ };
+ doAnswer(answer).when(mView).getLocationOnScreen(any());
+
+ assertTrue("Touch is within the view",
+ mSwipeHelper.isTouchInView(mEvent, mView));
+
+ doReturn(50f).when(mEvent).getRawX();
+
+ assertFalse("Touch is not within the view",
+ mSwipeHelper.isTouchInView(mEvent, mView));
+ }
+
+ @Test
+ public void testIsTouchInView_expandable() {
+ assertEquals("returns false when view is null", false,
+ NotificationSwipeHelper.isTouchInView(mEvent, null));
+
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
+
+ doReturn(20).when(mNotificationRow).getWidth();
+ doReturn(20).when(mNotificationRow).getActualHeight();
+
+ Answer answer = (Answer) invocation -> {
+ int[] arr = invocation.getArgument(0);
+ arr[0] = 0;
+ arr[1] = 0;
+ return null;
+ };
+ doAnswer(answer).when(mNotificationRow).getLocationOnScreen(any());
+
+ assertTrue("Touch is within the view",
+ mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
+
+ doReturn(50f).when(mEvent).getRawX();
+
+ assertFalse("Touch is not within the view",
+ mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
+ }
+}
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 a81d17f..1070795 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
@@ -82,28 +82,26 @@
// Remove should succeed because the notification is swiped out
mHeadsUpManager.removeNotification(mEntry.key, false /* releaseImmediately */);
- assertFalse(mHeadsUpManager.contains(mEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
}
@Test
- public void testShouldExtendLifetime_swipedOut() {
+ public void testCanRemoveImmediately_swipedOut() {
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.addSwipedOutNotification(mEntry.key);
- // Notification is swiped so its lifetime should not be extended even if it hasn't been
- // shown long enough
- assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ // Notification is swiped so it can be immediately removed.
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
}
@Test
- public void testShouldExtendLifetime_notTopEntry() {
+ public void testCanRemoveImmediately_notTopEntry() {
NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
laterEntry.row = mRow;
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
- // Notification is "behind" a higher priority notification so we have no reason to keep
- // its lifetime extended
- assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ // Notification is "behind" a higher priority notification so we can remove it immediately.
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
new file mode 100644
index 0000000..c792459
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.doReturn;
+
+import android.graphics.drawable.Drawable;
+import android.support.test.filters.SmallTest;
+import android.view.View;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.SysuiTestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.support.test.runner.AndroidJUnit4;
+
+/** atest NavigationBarContextTest */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NavigationBarContextTest extends SysuiTestCase {
+ private static final int GROUP_ID = 1;
+ private static final int BUTTON_0_ID = GROUP_ID + 1;
+ private static final int BUTTON_1_ID = GROUP_ID + 2;
+ private static final int BUTTON_2_ID = GROUP_ID + 3;
+
+ private static final float TEST_DARK_INTENSITY = 0.6f;
+ private static final float DARK_INTENSITY_ERR = 0.0002f;
+ private static final int ICON_RES_ID = 1;
+
+ private ContextualButtonGroup mGroup;
+ private ContextualButton mBtn0;
+ private ContextualButton mBtn1;
+ private ContextualButton mBtn2;
+
+ @Before
+ public void setup() {
+ mGroup = new ContextualButtonGroup(GROUP_ID);
+ mBtn0 = new ContextualButton(BUTTON_0_ID, ICON_RES_ID);
+ mBtn1 = new ContextualButton(BUTTON_1_ID, ICON_RES_ID);
+ mBtn2 = new ContextualButton(BUTTON_2_ID, ICON_RES_ID);
+
+ // Order of adding buttons to group determines the priority, ascending priority order
+ mGroup.addButton(mBtn0);
+ mGroup.addButton(mBtn1);
+ mGroup.addButton(mBtn2);
+ }
+
+ @Test
+ public void testAddGetContextButtons() throws Exception {
+ assertEquals(mBtn0, mGroup.getContextButton(BUTTON_0_ID));
+ assertEquals(mBtn1, mGroup.getContextButton(BUTTON_1_ID));
+ assertEquals(mBtn2, mGroup.getContextButton(BUTTON_2_ID));
+ }
+
+ @Test
+ public void testSetButtonVisibility() throws Exception {
+ assertFalse("By default the group should be invisible.", mGroup.isVisible());
+
+ // Set button 1 to be visible, make sure it is the only visible button
+ showButton(mBtn1);
+ assertFalse(mBtn0.isVisible());
+ assertTrue(mBtn1.isVisible());
+ assertFalse(mBtn2.isVisible());
+
+ // Hide button 1 and make sure the group is also invisible
+ assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
+ assertFalse("No buttons are visible, group should also be hidden", mGroup.isVisible());
+ assertNull("No buttons should be visible", mGroup.getVisibleContextButton());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSetButtonVisibilityUnaddedButton() throws Exception {
+ int id = mBtn2.getId() + 1;
+ mGroup.setButtonVisiblity(id, true /* visible */);
+ fail("Did not throw when setting a button with an invalid id");
+ }
+
+ @Test
+ public void testSetHigherPriorityButton() throws Exception {
+ // Show button 0
+ showButton(mBtn0);
+
+ // Show button 1
+ showButton(mBtn1);
+ assertTrue("Button 0 should be visible behind",
+ mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+
+ // Show button 2
+ showButton(mBtn2);
+ assertTrue("Button 1 should be visible behind",
+ mGroup.isButtonVisibleWithinGroup(mBtn1.getId()));
+ assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+ assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn1.getId()));
+ assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
+
+ // Hide button 2
+ assertNotEquals(mGroup.setButtonVisiblity(BUTTON_2_ID, false /* visible */), View.VISIBLE);
+ assertEquals("Hiding button 2 should show button 1", mBtn1,
+ mGroup.getVisibleContextButton());
+
+ // Hide button 1
+ assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
+ assertEquals("Hiding button 1 should show button 0", mBtn0,
+ mGroup.getVisibleContextButton());
+
+ // Hide button 0, all buttons are now invisible
+ assertNotEquals(mGroup.setButtonVisiblity(BUTTON_0_ID, false /* visible */), View.VISIBLE);
+ assertFalse("No buttons are visible, group should also be invisible", mGroup.isVisible());
+ assertNull(mGroup.getVisibleContextButton());
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn1.getId()));
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
+ }
+
+ @Test
+ public void testSetLowerPriorityButton() throws Exception {
+ // Show button 2
+ showButton(mBtn2);
+
+ // Show button 1
+ assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, true /* visible */), View.VISIBLE);
+ assertTrue("Showing button 1 lower priority should be hidden but visible underneath",
+ mGroup.isButtonVisibleWithinGroup(BUTTON_1_ID));
+ assertFalse(mBtn0.isVisible());
+ assertFalse(mBtn1.isVisible());
+ assertTrue(mBtn2.isVisible());
+
+ // Hide button 1
+ assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
+ assertFalse("Hiding button 1 with lower priority hides itself underneath",
+ mGroup.isButtonVisibleWithinGroup(BUTTON_1_ID));
+ assertTrue("A button still visible, group should also be visible", mGroup.isVisible());
+ assertEquals(mBtn2, mGroup.getVisibleContextButton());
+ }
+
+ @Test
+ public void testSetSamePriorityButton() throws Exception {
+ // Show button 1
+ showButton(mBtn1);
+
+ // Show button 1 again
+ showButton(mBtn1);
+
+ // The original button should still be visible
+ assertEquals(mBtn1, mGroup.getVisibleContextButton());
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
+ }
+
+ @Test
+ public void testUpdateIconsDarkIntensity() throws Exception {
+ final int unusedColor = 0;
+ final Drawable d = mock(Drawable.class);
+ final ContextualButton button = spy(mBtn0);
+ final KeyButtonDrawable kbd1 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor));
+ final KeyButtonDrawable kbd2 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor));
+ kbd1.setDarkIntensity(TEST_DARK_INTENSITY);
+ kbd2.setDarkIntensity(0f);
+
+ // Update icon returns the drawable intensity to half
+ doReturn(kbd1).when(button).getNewDrawable();
+ button.updateIcon();
+ assertEquals(TEST_DARK_INTENSITY, kbd1.getDarkIntensity(), DARK_INTENSITY_ERR);
+
+ // Return old dark intensity on new drawable after update icon
+ doReturn(kbd2).when(button).getNewDrawable();
+ button.updateIcon();
+ assertEquals(TEST_DARK_INTENSITY, kbd2.getDarkIntensity(), DARK_INTENSITY_ERR);
+ }
+
+ private void showButton(ContextualButton button) {
+ assertEquals(View.VISIBLE, mGroup.setButtonVisiblity(button.getId(), true /* visible */));
+ assertTrue("After set a button visible, group should also be visible", mGroup.isVisible());
+ assertEquals(button, mGroup.getVisibleContextButton());
+ }
+}
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 6a3c8a8..464f74b 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
@@ -36,6 +36,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -60,15 +61,23 @@
private static final String TEST_CHANNEL_ID = "test_channel";
private static final String TEST_GROUP_ID = "test_group";
private static final String TEST_PACKAGE_NAME = "test_pkg";
- private NotificationGroupManager mGroupManager = new NotificationGroupManager();
+ private NotificationGroupManager mGroupManager;
private int mId = 0;
@Mock HeadsUpManager mHeadsUpManager;
+ @Mock AmbientPulseManager mAmbientPulseManager;
@Before
public void setup() {
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+
+ initializeGroupManager();
+ }
+
+ private void initializeGroupManager() {
+ mGroupManager = new NotificationGroupManager();
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
}
@Test
@@ -141,8 +150,7 @@
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(createChildNotification());
- when(childEntry.row.isHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.contains(childEntry.key)).thenReturn(true);
+ when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true);
mGroupManager.onHeadsUpStateChanged(childEntry, true);
@@ -154,17 +162,35 @@
}
@Test
+ public void testAmbientPulseEntryIsIsolated() {
+ mGroupManager.setDozing(true);
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+ when(mAmbientPulseManager.isAlerting(childEntry.key)).thenReturn(true);
+
+ mGroupManager.onAmbientStateChanged(childEntry, true);
+
+ // 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));
+ }
+
+ @Test
public void testSuppressedSummaryHeadsUpTransfersToChild() {
NotificationData.Entry summaryEntry = createSummaryNotification();
- when(summaryEntry.row.isHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.contains(summaryEntry.key)).thenReturn(true);
+ when(mHeadsUpManager.isAlerting(summaryEntry.key)).thenReturn(true);
NotificationData.Entry childEntry = createChildNotification();
- // Summary will be suppressed because there is only one child
+ // Summary will be suppressed because there is only one child.
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- // A suppressed summary should transfer its heads up state to the child
+ // A suppressed summary should transfer its heads up state to the child.
verify(mHeadsUpManager, never()).showNotification(summaryEntry);
verify(mHeadsUpManager).showNotification(childEntry);
}
@@ -175,24 +201,64 @@
mGroupManager.setHeadsUpManager(mHeadsUpManager);
NotificationData.Entry summaryEntry =
createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- when(summaryEntry.row.isHeadsUp()).thenReturn(true);
NotificationData.Entry childEntry =
createChildNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry2 =
createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of heads up state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- when(summaryEntry.row.isHeadsUp()).thenReturn(false);
- when(childEntry.row.isHeadsUp()).thenReturn(true);
// Add second child notification so that summary is no longer suppressed.
mGroupManager.onEntryAdded(childEntry2);
// The heads up state should transfer back to the summary as there is now more than one
// child and the summary should no longer be suppressed.
- assertTrue(mHeadsUpManager.contains(summaryEntry.key));
- assertFalse(mHeadsUpManager.contains(childEntry.key));
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryAmbientPulseTransfersToChild() {
+ mGroupManager.setDozing(true);
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ when(mAmbientPulseManager.isAlerting(summaryEntry.key)).thenReturn(true);
+ NotificationData.Entry childEntry = createChildNotification();
+
+ // Summary will be suppressed because there is only one child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // A suppressed summary should transfer its ambient state to the child.
+ verify(mAmbientPulseManager, never()).showNotification(summaryEntry);
+ verify(mAmbientPulseManager).showNotification(childEntry);
+ }
+
+ @Test
+ public void testSuppressedSummaryAmbientPulseTransfersToChildButBackAgain() {
+ mGroupManager.setDozing(true);
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+ initializeGroupManager();
+ NotificationData.Entry summaryEntry =
+ createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mAmbientPulseManager.showNotification(summaryEntry);
+ // Trigger a transfer of ambient state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // The ambient state should transfer back to the summary as there is now more than one
+ // child and the summary should no longer be suppressed.
+ assertTrue(mAmbientPulseManager.isAlerting(summaryEntry.key));
+ assertFalse(mAmbientPulseManager.isAlerting(childEntry.key));
}
private NotificationData.Entry createSummaryNotification() {
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 cbba251..da93327 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
@@ -50,6 +50,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.support.test.metricshelper.MetricsAsserts;
@@ -119,9 +120,9 @@
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
- @Mock private SystemServicesProxy mSystemServicesProxy;
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
+ @Mock private IDreamManager mDreamManager;
@Mock private ScrimController mScrimController;
@Mock private ArrayList<Entry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@@ -194,8 +195,7 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mEntryManager = new TestableNotificationEntryManager(mSystemServicesProxy, mPowerManager,
- mContext);
+ mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
@@ -358,12 +358,12 @@
}
@Test
- public void testShouldPeek_nonSuppressedGroupSummary() {
+ public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
- when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+ when(mDreamManager.isDreaming()).thenReturn(false);
when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
Notification n = new Notification.Builder(getContext(), "a")
@@ -375,16 +375,16 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mEntryManager.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_suppressedGroupSummary() {
+ public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
- when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+ when(mDreamManager.isDreaming()).thenReturn(false);
when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
Notification n = new Notification.Builder(getContext(), "a")
@@ -396,15 +396,15 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mEntryManager.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_suppressedPeek() {
+ public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
- when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+ when(mDreamManager.isDreaming()).thenReturn(false);
when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
@@ -414,15 +414,15 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mEntryManager.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_noSuppressedPeek() {
+ public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
- when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+ when(mDreamManager.isDreaming()).thenReturn(false);
when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
@@ -432,31 +432,31 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mEntryManager.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testPeek_disabledStatusBar() {
+ public void testHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
- assertFalse("The panel shouldn't allow peek while disabled",
- mStatusBar.shouldPeek(entry, sbn));
+ assertFalse("The panel shouldn't allow heads up while disabled",
+ mStatusBar.canHeadsUp(entry, sbn));
}
@Test
- public void testPeek_disabledNotificationShade() {
+ public void testHeadsUp_disabledNotificationShade() {
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
- assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
- mStatusBar.shouldPeek(entry, sbn));
+ assertFalse("The panel shouldn't allow heads up while notification shade disabled",
+ mStatusBar.canHeadsUp(entry, sbn));
}
@Test
@@ -472,7 +472,7 @@
}
@Test
- public void testPanelOpenForPeek() {
+ public void testPanelOpenForHeadsUp() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
when(mNotificationList.size()).thenReturn(5);
@@ -690,10 +690,10 @@
public static class TestableNotificationEntryManager extends NotificationEntryManager {
- public TestableNotificationEntryManager(SystemServicesProxy systemServicesProxy,
+ public TestableNotificationEntryManager(IDreamManager dreamManager,
PowerManager powerManager, Context context) {
super(context);
- mSystemServicesProxy = systemServicesProxy;
+ mDreamManager = dreamManager;
mPowerManager = powerManager;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index b22a646..1cceefa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 0a83a89..5f54bce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -14,12 +14,11 @@
package com.android.systemui.utils.leaks;
-import android.content.Context;
import android.testing.LeakCheck;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
public class FakePluginManager implements PluginManager {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index ecda9620..f479126 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -20,7 +20,7 @@
import android.util.ArrayMap;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 41b3feb..5e87707 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -4026,6 +4026,8 @@
// - AUTOFILL_DATASET_AUTHENTICATED
// - AUTOFILL_INVALID_AUTHENTICATION
// - AUTOFILL_INVALID_DATASET_AUTHENTICATION
+ // NOTE: starting on OS Q, it also added the following fields:
+ // Tag FIELD_AUTOFILL_TEXT_LEN: length of the error message provided by the service
AUTOFILL_REQUEST = 907;
// Tag of a field for a package of an autofill service
@@ -4102,6 +4104,8 @@
// Tag FIELD_CLASS_NAME: Class name of the activity that is autofilled.
// Tag FIELD_AUTOFILL_SESSION_ID: id of the autofill session associated with this metric.
// Tag FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
+ // NOTE: starting on OS Q, it also added the following fields:
+ // Tag FIELD_AUTOFILL_TEXT_LEN: length of the error message provided by the service
AUTOFILL_DATA_SAVE_REQUEST = 918;
// An auto-fill session was finished
@@ -5922,7 +5926,7 @@
// Tag used to determine what type of charging was started/ended
// 1 = Plugged AC
// 2 = Plugged USB
- // 3 = Wireless
+ // 4 = Wireless
FIELD_PLUG_TYPE = 1421;
// ACTION: USB-C Connector connected.
@@ -6443,7 +6447,7 @@
// OPEN: Settings > System > Input & Gesture > Reach up gesture
// OS: Q
- SETTINGS_GESTURE_REACH = 1557;
+ SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557;
// OPEN: Emergency dialer opened
// CLOSE: Emergency dialer closed
@@ -6517,6 +6521,34 @@
// OS: Q
MOBILE_NETWORK = 1571;
+ // Tag of a field for the length of a text
+ FIELD_AUTOFILL_TEXT_LEN = 1572;
+
+ // Action: the notification assistant is changing a notification
+ // OS: Q
+ NOTIFICATION_ASSISTANT_ADJUSTMENT = 1573;
+
+ // Subtype: The people attached to a notification was changed
+ ADJUSTMENT_KEY_PEOPLE = 1574;
+
+ // Subtype: The snooze options attached to a notification was changed
+ ADJUSTMENT_KEY_SNOOZE_CRITERIA = 1575;
+
+ // Subtype: The group of a notification was changed
+ ADJUSTMENT_KEY_GROUP_KEY = 1576;
+
+ // Subtype: The user sentiment of a notification was changed
+ ADJUSTMENT_KEY_USER_SENTIMENT = 1577;
+
+ // Subtype: New actions have been added to a notification
+ ADJUSTMENT_KEY_SMART_ACTIONS = 1578;
+
+ // Subtype: New smart replies have been added to a notification
+ ADJUSTMENT_KEY_SMART_REPLIES = 1579;
+
+ // Subtype: The importance of a notification has been changed
+ ADJUSTMENT_KEY_IMPORTANCE = 1580;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b71d7a7..da52d40 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1834,7 +1834,7 @@
if (provider.widgets.isEmpty()) {
// cancel the future updates
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
// send the broacast saying that the provider is not in use any more
sendDisabledIntentLocked(provider);
@@ -1843,18 +1843,16 @@
}
}
- private void cancelBroadcasts(Provider provider) {
+ private void cancelBroadcastsLocked(Provider provider) {
if (DEBUG) {
- Slog.i(TAG, "cancelBroadcasts() for " + provider);
+ Slog.i(TAG, "cancelBroadcastsLocked() for " + provider);
}
if (provider.broadcast != null) {
- mAlarmManager.cancel(provider.broadcast);
- long token = Binder.clearCallingIdentity();
- try {
- provider.broadcast.cancel();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final PendingIntent broadcast = provider.broadcast;
+ mSaveStateHandler.post(() -> {
+ mAlarmManager.cancel(broadcast);
+ broadcast.cancel();
+ });
provider.broadcast = null;
}
}
@@ -2315,7 +2313,7 @@
mProviders.remove(provider);
// no need to send the DISABLE broadcast, since the receiver is gone anyway
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
}
private void sendEnableIntentLocked(Provider p) {
@@ -2369,17 +2367,14 @@
Binder.restoreCallingIdentity(token);
}
if (!alreadyRegistered) {
- long period = provider.info.updatePeriodMillis;
- if (period < MIN_UPDATE_PERIOD) {
- period = MIN_UPDATE_PERIOD;
- }
- final long oldId = Binder.clearCallingIdentity();
- try {
+ // Set the alarm outside of our locks; we've latched the first-time
+ // invariant and established the PendingIntent safely.
+ final long period = Math.max(provider.info.updatePeriodMillis, MIN_UPDATE_PERIOD);
+ final PendingIntent broadcast = provider.broadcast;
+ mSaveStateHandler.post(() ->
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, provider.broadcast);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ SystemClock.elapsedRealtime() + period, period, broadcast)
+ );
}
}
}
@@ -3382,7 +3377,7 @@
// Reschedule for the new updatePeriodMillis (don't worry about handling
// it specially if updatePeriodMillis didn't change because we just sent
// an update, and the next one will be updatePeriodMillis from now).
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
registerForBroadcastsLocked(provider, appWidgetIds);
// If it's currently showing, call back with the new
// AppWidgetProviderInfo.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 8c8352f..78facf8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -235,6 +235,13 @@
}
}
+ int getTargedSdkLocked() {
+ if (mInfo == null) {
+ return 0;
+ }
+ return mInfo.getServiceInfo().applicationInfo.targetSdkVersion;
+ }
+
private boolean isSetupCompletedLocked() {
final String setupComplete = Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
@@ -953,6 +960,7 @@
pw.println();
mInfo.dump(prefix2, pw);
pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel());
+ pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
}
pw.print(prefix); pw.print("Component from settings: ");
pw.println(getComponentNameFromSettings());
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 2671327..d1b09ca 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.ServiceConnection;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c9eb2d2..cf323fb 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -50,6 +50,7 @@
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -63,6 +64,7 @@
import android.service.autofill.Dataset;
import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
+import android.text.TextUtils;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -744,12 +746,17 @@
private void onFillRequestFailureOrTimeout(int requestId, boolean timedOut,
@Nullable CharSequence message) {
+ boolean showMessage = !TextUtils.isEmpty(message);
synchronized (mLock) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
+ ") rejected - session: " + id + " destroyed");
return;
}
+ if (sDebug) {
+ Slog.d(TAG, "finishing session due to service "
+ + (timedOut ? "timeout" : "failure"));
+ }
mService.resetLastResponse();
final LogMaker requestLog = mRequestLogs.get(requestId);
if (requestLog == null) {
@@ -757,8 +764,21 @@
} else {
requestLog.setType(timedOut ? MetricsEvent.TYPE_CLOSE : MetricsEvent.TYPE_FAILURE);
}
+ if (showMessage) {
+ final int targetSdk = mService.getTargedSdkLocked();
+ if (targetSdk >= Build.VERSION_CODES.Q) {
+ showMessage = false;
+ Slog.w(TAG, "onFillRequestFailureOrTimeout(): not showing '" + message
+ + "' because service's targetting API " + targetSdk);
+ }
+ if (message != null) {
+ requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN,
+ message.length());
+ }
+ }
}
- if (message != null) {
+ notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED);
+ if (showMessage) {
getUiForShowing().showError(message, this);
}
removeSelf();
@@ -793,6 +813,7 @@
@Override
public void onSaveRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName) {
+ boolean showMessage = !TextUtils.isEmpty(message);
synchronized (mLock) {
mIsSaving = false;
@@ -801,12 +822,26 @@
+ id + " destroyed");
return;
}
+ if (showMessage) {
+ final int targetSdk = mService.getTargedSdkLocked();
+ if (targetSdk >= Build.VERSION_CODES.Q) {
+ showMessage = false;
+ Slog.w(TAG, "onSaveRequestFailure(): not showing '" + message
+ + "' because service's targetting API " + targetSdk);
+ }
+ }
}
- LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
.setType(MetricsEvent.TYPE_FAILURE);
+ if (message != null) {
+ log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length());
+ }
mMetricsLogger.write(log);
- getUiForShowing().showError(message, this);
+ if (showMessage) {
+ getUiForShowing().showError(message, this);
+ }
removeSelf();
}
@@ -1206,7 +1241,7 @@
// - not autofilled but matches a dataset value -> manuallyFilledIds
if ((state & ViewState.STATE_CHANGED) != 0) {
// Check if autofilled value was changed
- if ((state & ViewState.STATE_AUTOFILLED) != 0) {
+ if ((state & ViewState.STATE_AUTOFILLED_ONCE) != 0) {
final String datasetId = viewState.getDatasetId();
if (datasetId == null) {
// Sanity check - should never happen.
@@ -2181,12 +2216,28 @@
// Must check if this update was caused by autofilling the view, in which
// case we just update the value, but not the UI.
final AutofillValue filledValue = viewState.getAutofilledValue();
- if (filledValue != null && filledValue.equals(value)) {
- if (sVerbose) {
- Slog.v(TAG, "ignoring autofilled change on id " + id);
+ if (filledValue != null) {
+ if (filledValue.equals(value)) {
+ if (sVerbose) {
+ Slog.v(TAG, "ignoring autofilled change on id " + id);
+ }
+ viewState.resetState(ViewState.STATE_CHANGED);
+ return;
}
- return;
+ else {
+ if ((viewState.id.equals(this.mCurrentViewId)) &&
+ (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) {
+ // Remove autofilled state once field is changed after autofilling.
+ if (sVerbose) {
+ Slog.v(TAG, "field changed after autofill on id " + id);
+ }
+ viewState.resetState(ViewState.STATE_AUTOFILLED);
+ final ViewState currentView = mViewStates.get(mCurrentViewId);
+ currentView.maybeCallOnFillReady(flags);
+ }
+ }
}
+
// Update the internal state...
viewState.setState(ViewState.STATE_CHANGED);
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index e6cd7e0..a8dae03 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -69,8 +69,10 @@
public static final int STATE_RESTARTED_SESSION = 0x100;
/** View is the URL bar of a package on compat mode. */
public static final int STATE_URL_BAR = 0x200;
- /** View was asked to autofil but failed to do so. */
+ /** View was asked to autofill but failed to do so. */
public static final int STATE_AUTOFILL_FAILED = 0x400;
+ /** View has been autofilled at least once. */
+ public static final int STATE_AUTOFILLED_ONCE = 0x800;
public final AutofillId id;
@@ -161,6 +163,9 @@
} else {
mState |= state;
}
+ if (state == STATE_AUTOFILLED) {
+ mState |= STATE_AUTOFILLED_ONCE;
+ }
}
void resetState(int state) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 5962406..fe86ab3 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -43,6 +43,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
+import com.android.server.UiModeManagerInternal;
import com.android.server.UiThread;
import com.android.server.autofill.Helper;
@@ -69,6 +71,7 @@
private final MetricsLogger mMetricsLogger = new MetricsLogger();
private final @NonNull OverlayControl mOverlayControl;
+ private final @NonNull UiModeManagerInternal mUiModeMgr;
public interface AutoFillUiCallback {
void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent,
@@ -86,6 +89,7 @@
public AutoFillUI(@NonNull Context context) {
mContext = context;
mOverlayControl = new OverlayControl(context);
+ mUiModeMgr = LocalServices.getService(UiModeManagerInternal.class);
}
public void setCallback(@NonNull AutoFillUiCallback callback) {
@@ -193,7 +197,9 @@
}
hideAllUiThread(callback);
mFillUi = new FillUi(mContext, response, focusedId,
- filterText, mOverlayControl, serviceLabel, serviceIcon, new FillUi.Callback() {
+ filterText, mOverlayControl, serviceLabel, serviceIcon,
+ mUiModeMgr.isNightMode(),
+ new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
log.setType(MetricsEvent.TYPE_DETAIL);
@@ -332,7 +338,7 @@
}
mMetricsLogger.write(log);
}
- }, isUpdate, compatMode);
+ }, mUiModeMgr.isNightMode(), isUpdate, compatMode);
});
}
@@ -368,6 +374,7 @@
pw.println("Autofill UI");
final String prefix = " ";
final String prefix2 = " ";
+ pw.print(prefix); pw.print("Night mode: "); pw.println(mUiModeMgr.isNightMode());
if (mFillUi != null) {
pw.print(prefix); pw.println("showsFillUi: true");
mFillUi.dump(pw, prefix2);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 68a495f..742d494 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -73,7 +73,10 @@
final class FillUi {
private static final String TAG = "FillUi";
- private static final int THEME_ID = com.android.internal.R.style.Theme_DeviceDefault_Autofill;
+ private static final int THEME_ID_LIGHT =
+ com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill;
+ private static final int THEME_ID_DARK =
+ com.android.internal.R.style.Theme_DeviceDefault_Autofill;
private static final TypedValue sTempTypedValue = new TypedValue();
@@ -117,6 +120,8 @@
private boolean mDestroyed;
+ private final int mThemeId;
+
public static boolean isFullScreen(Context context) {
if (sFullScreenMode != null) {
if (sVerbose) Slog.v(TAG, "forcing full-screen mode to " + sFullScreenMode);
@@ -128,10 +133,13 @@
FillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
@NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
- @NonNull Drawable serviceIcon, @NonNull Callback callback) {
+ @NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) {
+ if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
+ mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
mCallback = callback;
mFullScreen = isFullScreen(context);
- mContext = new ContextThemeWrapper(context, THEME_ID);
+ mContext = new ContextThemeWrapper(context, mThemeId);
+
final LayoutInflater inflater = LayoutInflater.from(mContext);
final RemoteViews headerPresentation = response.getHeader();
@@ -216,7 +224,7 @@
ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
final View content;
try {
- response.getPresentation().setApplyTheme(THEME_ID);
+ response.getPresentation().setApplyTheme(mThemeId);
content = response.getPresentation().apply(mContext, decor, interceptionHandler);
container.addView(content);
} catch (RuntimeException e) {
@@ -257,7 +265,7 @@
RemoteViews.OnClickHandler clickBlocker = null;
if (headerPresentation != null) {
clickBlocker = newClickBlocker();
- headerPresentation.setApplyTheme(THEME_ID);
+ headerPresentation.setApplyTheme(mThemeId);
mHeader = headerPresentation.apply(mContext, null, clickBlocker);
final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header);
@@ -275,7 +283,7 @@
if (clickBlocker == null) { // already set for header
clickBlocker = newClickBlocker();
}
- footerPresentation.setApplyTheme(THEME_ID);
+ footerPresentation.setApplyTheme(mThemeId);
mFooter = footerPresentation.apply(mContext, null, clickBlocker);
// Footer not supported on some platform e.g. TV
if (sVerbose) Slog.v(TAG, "adding footer");
@@ -302,7 +310,7 @@
final View view;
try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
- presentation.setApplyTheme(THEME_ID);
+ presentation.setApplyTheme(mThemeId);
view = presentation.apply(mContext, null, interceptionHandler);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
@@ -732,6 +740,18 @@
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
+ switch (mThemeId) {
+ case THEME_ID_DARK:
+ pw.println(" (dark)");
+ break;
+ case THEME_ID_LIGHT:
+ pw.println(" (light)");
+ break;
+ default:
+ pw.println("(UNKNOWN_MODE)");
+ break;
+ }
if (mWindow != null) {
pw.print(prefix); pw.print("mWindow: ");
final String prefix2 = prefix + " ";
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 9d3d3cb..89b442e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -72,9 +72,11 @@
*/
final class SaveUi {
- private static final String TAG = "AutofillSaveUi";
+ private static final String TAG = "SaveUi";
- private static final int THEME_ID =
+ private static final int THEME_ID_LIGHT =
+ com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill_Save;
+ private static final int THEME_ID_DARK =
com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
public interface OnSaveListener {
@@ -144,6 +146,7 @@
private final String mServicePackageName;
private final ComponentName mComponentName;
private final boolean mCompatMode;
+ private final int mThemeId;
private boolean mDestroyed;
@@ -152,7 +155,9 @@
@Nullable String servicePackageName, @NonNull ComponentName componentName,
@NonNull SaveInfo info, @NonNull ValueFinder valueFinder,
@NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener,
- boolean isUpdate, boolean compatMode) {
+ boolean nightMode, boolean isUpdate, boolean compatMode) {
+ if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
+ mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
mPendingUi= pendingUi;
mListener = new OneActionThenDestroyListener(listener);
mOverlayControl = overlayControl;
@@ -160,7 +165,7 @@
mComponentName = componentName;
mCompatMode = compatMode;
- context = new ContextThemeWrapper(context, THEME_ID);
+ context = new ContextThemeWrapper(context, mThemeId);
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -250,7 +255,7 @@
}
yesButton.setOnClickListener((v) -> mListener.onSave());
- mDialog = new Dialog(context, THEME_ID);
+ mDialog = new Dialog(context, mThemeId);
mDialog.setContentView(view);
// Dialog can be dismissed when touched outside, but the negative listener should not be
@@ -337,7 +342,7 @@
try {
// Create the remote view peer.
- template.setApplyTheme(THEME_ID);
+ template.setApplyTheme(mThemeId);
final View customSubtitleView = template.apply(context, null, handler);
// Apply batch updates (if any).
@@ -556,7 +561,18 @@
pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName);
pw.print(prefix); pw.print("app: "); pw.println(mComponentName.toShortString());
pw.print(prefix); pw.print("compat mode: "); pw.println(mCompatMode);
-
+ pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
+ switch (mThemeId) {
+ case THEME_ID_DARK:
+ pw.println(" (dark)");
+ break;
+ case THEME_ID_LIGHT:
+ pw.println(" (light)");
+ break;
+ default:
+ pw.println("(UNKNOWN_MODE)");
+ break;
+ }
final View view = mDialog.getWindow().getDecorView();
final int[] loc = view.getLocationOnScreen();
pw.print(prefix); pw.print("coordinates: ");
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java b/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java
new file mode 100644
index 0000000..5bec1a9
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java
@@ -0,0 +1,54 @@
+package com.android.server.backup.encryption.chunk;
+
+import android.util.proto.ProtoInputStream;
+
+import java.io.IOException;
+
+/**
+ * Information about a chunk entry in a protobuf. Only used for reading from a {@link
+ * ProtoInputStream}.
+ */
+public class Chunk {
+ /**
+ * Reads a Chunk from a {@link ProtoInputStream}. Expects the message to be of format {@link
+ * ChunksMetadataProto.Chunk}.
+ *
+ * @param inputStream currently at a {@link ChunksMetadataProto.Chunk} message.
+ * @throws IOException when the message is not structured as expected or a field can not be
+ * read.
+ */
+ static Chunk readFromProto(ProtoInputStream inputStream) throws IOException {
+ Chunk result = new Chunk();
+
+ while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (inputStream.getFieldNumber()) {
+ case (int) ChunksMetadataProto.Chunk.HASH:
+ result.mHash = inputStream.readBytes(ChunksMetadataProto.Chunk.HASH);
+ break;
+ case (int) ChunksMetadataProto.Chunk.LENGTH:
+ result.mLength = inputStream.readInt(ChunksMetadataProto.Chunk.LENGTH);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ private int mLength;
+ private byte[] mHash;
+
+ /** Private constructor. This class should only be instantiated by calling readFromProto. */
+ private Chunk() {
+ // Set default values for fields in case they are not available in the proto.
+ mHash = new byte[]{};
+ mLength = 0;
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public byte[] getHash() {
+ return mHash;
+ }
+}
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java
new file mode 100644
index 0000000..2d2e88a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.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.backup.encryption.chunk;
+
+import android.annotation.Nullable;
+import android.util.proto.ProtoInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Chunk listing in a format optimized for quick look-up of chunks via their hash keys. This is
+ * useful when building an incremental backup. After a chunk has been produced, the algorithm can
+ * quickly look up whether the chunk existed in the previous backup by checking this chunk listing.
+ * It can then tell the server to use that chunk, through telling it the position and length of the
+ * chunk in the previous backup's blob.
+ */
+public class ChunkListing {
+ /**
+ * Reads a ChunkListing from a {@link ProtoInputStream}. Expects the message to be of format
+ * {@link ChunksMetadataProto.ChunkListing}.
+ *
+ * @param inputStream Currently at a {@link ChunksMetadataProto.ChunkListing} message.
+ * @throws IOException when the message is not structured as expected or a field can not be
+ * read.
+ */
+ public static ChunkListing readFromProto(ProtoInputStream inputStream) throws IOException {
+ Map<ChunkHash, Entry> entries = new HashMap();
+
+ long start = 0;
+
+ while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (inputStream.getFieldNumber() == (int) ChunksMetadataProto.ChunkListing.CHUNKS) {
+ long chunkToken = inputStream.start(ChunksMetadataProto.ChunkListing.CHUNKS);
+ Chunk chunk = Chunk.readFromProto(inputStream);
+ entries.put(new ChunkHash(chunk.getHash()), new Entry(start, chunk.getLength()));
+ start += chunk.getLength();
+ inputStream.end(chunkToken);
+ }
+ }
+
+ return new ChunkListing(entries);
+ }
+
+ private final Map<ChunkHash, Entry> mChunksByHash;
+
+ private ChunkListing(Map<ChunkHash, Entry> chunksByHash) {
+ mChunksByHash = Collections.unmodifiableMap(new HashMap<>(chunksByHash));
+ }
+
+ /** Returns {@code true} if there is a chunk with the given SHA-256 MAC key in the listing. */
+ public boolean hasChunk(ChunkHash hash) {
+ return mChunksByHash.containsKey(hash);
+ }
+
+ /**
+ * Returns the entry for the chunk with the given hash.
+ *
+ * @param hash The SHA-256 MAC of the plaintext of the chunk.
+ * @return The entry, containing position and length of the chunk in the backup blob, or null if
+ * it does not exist.
+ */
+ @Nullable
+ public Entry getChunkEntry(ChunkHash hash) {
+ return mChunksByHash.get(hash);
+ }
+
+ /** Returns the number of chunks in this listing. */
+ public int getChunkCount() {
+ return mChunksByHash.size();
+ }
+
+ /** Information about a chunk entry in a backup blob - i.e., its position and length. */
+ public static final class Entry {
+ private final int mLength;
+ private final long mStart;
+
+ private Entry(long start, int length) {
+ mStart = start;
+ mLength = length;
+ }
+
+ /** Returns the length of the chunk in bytes. */
+ public int getLength() {
+ return mLength;
+ }
+
+ /** Returns the start position of the chunk in the backup blob, in bytes. */
+ public long getStart() {
+ return mStart;
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java b/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
new file mode 100644
index 0000000..3a6d1f6
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.encryption.chunk;
+
+import java.util.Arrays;
+
+/**
+ * Holds the bytes of an encrypted {@link ChunksMetadataProto.ChunkOrdering}.
+ *
+ * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename
+ * encryptedChunkOrdering() to getBytes().
+ */
+public class EncryptedChunkOrdering {
+ /**
+ * Constructs a new object holding the given bytes of an encrypted {@link
+ * ChunksMetadataProto.ChunkOrdering}.
+ *
+ * <p>Note that this just holds an ordering which is already encrypted, it does not encrypt the
+ * ordering.
+ */
+ public static EncryptedChunkOrdering create(byte[] encryptedChunkOrdering) {
+ return new EncryptedChunkOrdering(encryptedChunkOrdering);
+ }
+
+ private final byte[] mEncryptedChunkOrdering;
+
+ public byte[] encryptedChunkOrdering() {
+ return mEncryptedChunkOrdering;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof EncryptedChunkOrdering)) {
+ return false;
+ }
+
+ EncryptedChunkOrdering encryptedChunkOrdering = (EncryptedChunkOrdering) o;
+ return Arrays.equals(
+ mEncryptedChunkOrdering, encryptedChunkOrdering.mEncryptedChunkOrdering);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mEncryptedChunkOrdering);
+ }
+
+ private EncryptedChunkOrdering(byte[] encryptedChunkOrdering) {
+ mEncryptedChunkOrdering = encryptedChunkOrdering;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/AgentException.java b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java
new file mode 100644
index 0000000..e2ca351
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/AgentException.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.server.backup.keyvalue;
+
+/**
+ * This represents something wrong with a specific package. For example:
+ * <ul>
+ * <li>Package unknown.
+ * <li>Package is not eligible for backup anymore.
+ * <li>Backup agent timed out.
+ * <li>Backup agent wrote protected keys.
+ * <li>...
+ * </ul>
+ *
+ * @see KeyValueBackupTask
+ * @see TaskException
+ */
+class AgentException extends BackupException {
+ static AgentException transitory() {
+ return new AgentException(/* transitory */ true);
+ }
+
+ static AgentException transitory(Exception cause) {
+ return new AgentException(/* transitory */ true, cause);
+ }
+
+ static AgentException permanent() {
+ return new AgentException(/* transitory */ false);
+ }
+
+ static AgentException permanent(Exception cause) {
+ return new AgentException(/* transitory */ false, cause);
+ }
+
+ private final boolean mTransitory;
+
+ private AgentException(boolean transitory) {
+ mTransitory = transitory;
+ }
+
+ private AgentException(boolean transitory, Exception cause) {
+ super(cause);
+ mTransitory = transitory;
+ }
+
+ boolean isTransitory() {
+ return mTransitory;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
copy to services/backup/java/com/android/server/backup/keyvalue/BackupException.java
index 5d19851..27b2d35 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -14,12 +14,20 @@
* limitations under the License
*/
-package com.android.systemui.stackdivider.events;
+package com.android.server.backup.keyvalue;
-import com.android.systemui.recents.events.EventBus;
+import android.util.AndroidException;
/**
- * Sent when the divider is being draged either manually or by an animation.
+ * Key-value backup task exception.
+ *
+ * @see AgentException
+ * @see TaskException
*/
-public class StartedDragingEvent extends EventBus.Event {
+class BackupException extends AndroidException {
+ BackupException() {}
+
+ BackupException(Exception cause) {
+ super(cause);
+ }
}
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 54e6b1d..bb8a1d1 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -54,7 +54,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 || true;
+ @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
static void onNewThread(String threadName) {
if (DEBUG) {
@@ -153,16 +153,18 @@
mObserver, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
}
- void onBindAgentError(SecurityException e) {
- Slog.d(TAG, "Error in bind/backup", e);
- }
-
void onAgentUnknown(String packageName) {
Slog.d(TAG, "Package does not exist, skipping");
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_PACKAGE_NOT_FOUND);
}
+ void onBindAgentError(String packageName, SecurityException e) {
+ Slog.d(TAG, "Error in bind/backup", e);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
+ }
+
void onAgentError(String packageName) {
if (MORE_DEBUG) {
Slog.i(TAG, "Agent failure for " + packageName + ", re-staging");
@@ -190,6 +192,8 @@
void onCallAgentDoBackupError(String packageName, boolean callingAgent, Exception e) {
if (callingAgent) {
Slog.e(TAG, "Error invoking agent on " + packageName + ": " + e);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
} else {
Slog.e(TAG, "Error before invoking agent on " + packageName + ": " + e);
}
@@ -220,12 +224,8 @@
}
}
- void onReadAgentDataError(String packageName, IOException e) {
- Slog.w(TAG, "Unable read backup data for " + packageName + ": " + e);
- }
-
- void onWriteWidgetDataError(String packageName, IOException e) {
- Slog.w(TAG, "Unable to save widget data for " + packageName + ": " + e);
+ void onAgentDataError(String packageName, IOException e) {
+ Slog.w(TAG, "Unable to read/write agent data for " + packageName + ": " + e);
}
void onDigestError(NoSuchAlgorithmException e) {
@@ -243,16 +243,12 @@
}
}
- void onSendDataToTransport(String packageName) {
+ void onTransportPerformBackup(String packageName) {
if (MORE_DEBUG) {
Slog.v(TAG, "Sending non-empty data to transport for " + packageName);
}
}
- void onNonIncrementalAndNonIncrementalRequired() {
- Slog.e(TAG, "Transport requested non-incremental but already the case");
- }
-
void onEmptyData(PackageInfo packageInfo) {
if (MORE_DEBUG) {
Slog.i(TAG, "No backup data written, not calling transport");
@@ -302,13 +298,20 @@
/* extras */ null);
}
+ void onPackageBackupNonIncrementalAndNonIncrementalRequired(String packageName) {
+ Slog.e(TAG, "Transport requested non-incremental but already the case");
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
+ }
+
void onPackageBackupTransportFailure(String packageName) {
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
}
- void onPackageBackupError(String packageName, Exception e) {
+ void onPackageBackupTransportError(String packageName, Exception e) {
Slog.e(TAG, "Transport error backing up " + packageName, e);
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
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 e915ce1..6904b3f 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -16,6 +16,7 @@
package com.android.server.backup.keyvalue;
+import static android.app.ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -25,8 +26,8 @@
import static com.android.server.backup.BackupManagerService.OP_PENDING;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -47,7 +48,6 @@
import android.os.SELinux;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -77,6 +77,8 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -173,10 +175,8 @@
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;
- @VisibleForTesting
- public static final String STAGING_FILE_SUFFIX = ".data";
- @VisibleForTesting
- public static final String NEW_STATE_FILE_SUFFIX = ".new";
+ @VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
+ @VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
/**
* Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
@@ -244,13 +244,13 @@
private final int mCurrentOpToken;
private final File mStateDirectory;
private final File mDataDirectory;
+ private final File mBlankStateFile;
private final List<String> mOriginalQueue;
private final List<String> mQueue;
private final List<String> mPendingFullBackups;
private final Object mQueueLock;
@Nullable private final DataChangedJournal mJournal;
- private int mStatus;
@Nullable private PerformFullTransportBackupTask mFullBackupTask;
@Nullable private IBackupAgent mAgent;
@Nullable private PackageInfo mCurrentPackage;
@@ -316,6 +316,7 @@
mDataDirectory = mBackupManagerService.getDataDir();
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mQueueLock = mBackupManagerService.getQueueLock();
+ mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
}
private void registerTask() {
@@ -331,45 +332,43 @@
public void run() {
Process.setThreadPriority(THREAD_PRIORITY);
- boolean processQueue = startTask();
- while (processQueue && !mQueue.isEmpty() && !mCancelled) {
- String packageName = mQueue.remove(0);
- if (PM_PACKAGE.equals(packageName)) {
- processQueue = backupPm();
- } else {
- processQueue = backupPackage(packageName);
+ int status = BackupTransport.TRANSPORT_OK;
+ try {
+ startTask();
+ while (!mQueue.isEmpty() && !mCancelled) {
+ String packageName = mQueue.remove(0);
+ try {
+ if (PM_PACKAGE.equals(packageName)) {
+ backupPm();
+ } else {
+ backupPackage(packageName);
+ }
+ } catch (AgentException e) {
+ if (e.isTransitory()) {
+ // We try again this package in the next backup pass.
+ mBackupManagerService.dataChangedImpl(packageName);
+ }
+ }
}
+ } catch (TaskException e) {
+ if (e.isStateCompromised()) {
+ mBackupManagerService.resetBackupState(mStateDirectory);
+ }
+ revertTask();
+ status = e.getStatus();
}
- finishTask();
+ finishTask(status);
}
- /** Returns whether to consume next queue package. */
- private boolean handleAgentResult(@Nullable PackageInfo packageInfo, RemoteResult result) {
- if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
- // Not an explicit cancel, we need to flag it.
- mCancelled = true;
- mReporter.onAgentCancelled(packageInfo);
- cleanUpAgentForAgentError();
- return false;
+ /** Returns transport status. */
+ private int sendDataToTransport(@Nullable PackageInfo packageInfo)
+ throws AgentException, TaskException {
+ try {
+ return sendDataToTransport();
+ } catch (IOException e) {
+ mReporter.onAgentDataError(packageInfo.packageName, e);
+ throw TaskException.causedBy(e);
}
- if (result == RemoteResult.FAILED_CANCELLED) {
- mReporter.onAgentCancelled(packageInfo);
- cleanUpAgentForAgentError();
- return false;
- }
- if (result == RemoteResult.FAILED_TIMED_OUT) {
- mReporter.onAgentTimedOut(packageInfo);
- cleanUpAgentForAgentError();
- return true;
- }
- Preconditions.checkState(result.isPresent());
- long agentResult = result.get();
- if (agentResult == BackupAgent.RESULT_ERROR) {
- mReporter.onAgentResultError(packageInfo);
- cleanUpAgentForAgentError();
- return true;
- }
- return sendDataToTransport();
}
@Override
@@ -378,11 +377,10 @@
@Override
public void operationComplete(long unusedResult) {}
- /** Returns whether to consume next queue package. */
- private boolean startTask() {
+ private void startTask() throws TaskException {
if (mBackupManagerService.isBackupOperationInProgress()) {
mReporter.onSkipBackup();
- return false;
+ throw TaskException.create();
}
// Unfortunately full backup task constructor registers the task with BMS, so we have to
@@ -390,11 +388,9 @@
mFullBackupTask = createFullBackupTask(mPendingFullBackups);
registerTask();
- mStatus = BackupTransport.TRANSPORT_OK;
-
if (mQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
mReporter.onEmptyQueueAtStart();
- return false;
+ return;
}
// We only backup PM if it was explicitly in the queue or if it's incremental.
boolean backupPm = mQueue.remove(PM_PACKAGE) || !mNonIncremental;
@@ -415,20 +411,18 @@
if (pmState.length() <= 0) {
mReporter.onInitializeTransport(transportName);
mBackupManagerService.resetBackupState(mStateDirectory);
- mStatus = transport.initializeDevice();
- mReporter.onTransportInitialized(mStatus);
+ int status = transport.initializeDevice();
+ mReporter.onTransportInitialized(status);
+ if (status != BackupTransport.TRANSPORT_OK) {
+ throw TaskException.stateCompromised();
+ }
}
+ } catch (TaskException e) {
+ throw e;
} catch (Exception e) {
mReporter.onInitializeTransportError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
+ throw TaskException.stateCompromised();
}
-
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- mBackupManagerService.resetBackupState(mStateDirectory);
- return false;
- }
-
- return true;
}
private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
@@ -446,120 +440,82 @@
mUserInitiated);
}
- /** Returns whether to consume next queue package. */
- private boolean backupPm() {
- RemoteResult agentResult = null;
+ private void backupPm() throws TaskException {
+ mReporter.onStartPackageBackup(PM_PACKAGE);
+ mCurrentPackage = new PackageInfo();
+ mCurrentPackage.packageName = PM_PACKAGE;
+
try {
- mCurrentPackage = new PackageInfo();
- mCurrentPackage.packageName = PM_PACKAGE;
-
- // Since PM is running in the system process we can set up its agent directly.
- BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
- mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
-
- Pair<Integer, RemoteResult> statusAndResult = extractAgentData(PM_PACKAGE, mAgent);
- mStatus = statusAndResult.first;
- agentResult = statusAndResult.second;
- } catch (Exception e) {
+ extractPmAgentData(mCurrentPackage);
+ int status = sendDataToTransport(mCurrentPackage);
+ cleanUpAgentForTransportStatus(status);
+ } catch (AgentException | TaskException e) {
mReporter.onExtractPmAgentDataError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
+ cleanUpAgentForError(e);
+ // PM agent failure is task failure.
+ throw TaskException.stateCompromised(e);
}
-
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- // In this case either extractAgentData() already made the agent clean-up or we haven't
- // prepared the state for calling the agent, in either case we don't need to clean-up.
- mBackupManagerService.resetBackupState(mStateDirectory);
- return false;
- }
-
- Preconditions.checkNotNull(agentResult);
- return handleAgentResult(mCurrentPackage, agentResult);
}
- /** Returns whether to consume next queue package. */
- private boolean backupPackage(String packageName) {
+ private void backupPackage(String packageName) throws AgentException, TaskException {
mReporter.onStartPackageBackup(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
+ mCurrentPackage = getPackageForBackup(packageName);
- // Verify that the requested app is eligible for key-value backup.
- RemoteResult agentResult = null;
try {
- mCurrentPackage = mPackageManager.getPackageInfo(
- packageName, PackageManager.GET_SIGNING_CERTIFICATES);
- ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo;
- if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
- // The manifest has changed. This won't happen again because the app won't be
- // requesting further backups.
- mReporter.onPackageNotEligibleForBackup(packageName);
- return true;
- }
-
- if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) {
- // Initially enqueued for key-value backup, but only supports full-backup now.
- mReporter.onPackageEligibleForFullBackup(packageName);
- return true;
- }
-
- if (AppBackupUtils.appIsStopped(applicationInfo)) {
- // Just as it won't receive broadcasts, we won't run it for backup.
- mReporter.onPackageStopped(packageName);
- return true;
- }
-
- try {
- mBackupManagerService.setWorkSource(new WorkSource(applicationInfo.uid));
- IBackupAgent agent =
- mBackupManagerService.bindToAgentSynchronous(
- applicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
- if (agent != null) {
- mAgent = agent;
- Pair<Integer, RemoteResult> statusAndResult =
- extractAgentData(packageName, agent);
- mStatus = statusAndResult.first;
- agentResult = statusAndResult.second;
- } else {
- // Timeout waiting for the agent to bind.
- mStatus = BackupTransport.AGENT_ERROR;
- }
- } catch (SecurityException e) {
- mReporter.onBindAgentError(e);
- mStatus = BackupTransport.AGENT_ERROR;
- }
- } catch (PackageManager.NameNotFoundException e) {
- mStatus = BackupTransport.AGENT_UNKNOWN;
- } finally {
- mBackupManagerService.setWorkSource(null);
+ extractAgentData(mCurrentPackage);
+ int status = sendDataToTransport(mCurrentPackage);
+ cleanUpAgentForTransportStatus(status);
+ } catch (AgentException | TaskException e) {
+ cleanUpAgentForError(e);
+ throw e;
}
-
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- // In this case either extractAgentData() already made the agent clean-up or we haven't
- // prepared the state for calling the agent, in either case we don't need to clean-up.
- Preconditions.checkState(mAgent == null);
-
- if (mStatus == BackupTransport.AGENT_ERROR) {
- mReporter.onAgentError(packageName);
- mBackupManagerService.dataChangedImpl(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- return true;
- }
-
- if (mStatus == BackupTransport.AGENT_UNKNOWN) {
- mReporter.onAgentUnknown(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- return true;
- }
-
- // Transport-level failure, re-enqueue everything.
- revertTask();
- return false;
- }
-
- Preconditions.checkNotNull(agentResult);
- return handleAgentResult(mCurrentPackage, agentResult);
}
- private void finishTask() {
+ private PackageInfo getPackageForBackup(String packageName) throws AgentException {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo =
+ mPackageManager.getPackageInfo(
+ packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+ } catch (PackageManager.NameNotFoundException e) {
+ mReporter.onAgentUnknown(packageName);
+ throw AgentException.permanent(e);
+ }
+ ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+ if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
+ mReporter.onPackageNotEligibleForBackup(packageName);
+ throw AgentException.permanent();
+ }
+ if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+ mReporter.onPackageEligibleForFullBackup(packageName);
+ throw AgentException.permanent();
+ }
+ if (AppBackupUtils.appIsStopped(applicationInfo)) {
+ mReporter.onPackageStopped(packageName);
+ throw AgentException.permanent();
+ }
+ return packageInfo;
+ }
+
+ private IBackupAgent bindAgent(PackageInfo packageInfo) throws AgentException {
+ String packageName = packageInfo.packageName;
+ final IBackupAgent agent;
+ try {
+ agent =
+ mBackupManagerService.bindToAgentSynchronous(
+ packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL);
+ if (agent == null) {
+ mReporter.onAgentError(packageName);
+ throw AgentException.transitory();
+ }
+ } catch (SecurityException e) {
+ mReporter.onBindAgentError(packageName, e);
+ throw AgentException.transitory(e);
+ }
+ return agent;
+ }
+
+ private void finishTask(int status) {
// Mark packages that we couldn't backup as pending backup.
for (String packageName : mQueue) {
mBackupManagerService.dataChangedImpl(packageName);
@@ -576,7 +532,7 @@
// If we succeeded and this is the first time we've done a backup, we can record the current
// backup dataset token.
long currentToken = mBackupManagerService.getCurrentToken();
- if ((mStatus == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
+ if ((status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
try {
IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
@@ -589,9 +545,14 @@
synchronized (mQueueLock) {
mBackupManagerService.setBackupRunning(false);
- if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
+ if (status == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
mReporter.onTransportNotInitialized();
- triggerTransportInitializationLocked();
+ try {
+ triggerTransportInitializationLocked();
+ } catch (Exception e) {
+ mReporter.onPendingInitializeTransportError(e);
+ status = BackupTransport.TRANSPORT_ERROR;
+ }
}
}
@@ -605,7 +566,7 @@
}
if (!mCancelled
- && mStatus == BackupTransport.TRANSPORT_OK
+ && status == BackupTransport.TRANSPORT_OK
&& mFullBackupTask != null
&& !mPendingFullBackups.isEmpty()) {
mReporter.onStartFullBackup(mPendingFullBackups);
@@ -621,7 +582,7 @@
mFullBackupTask.unregisterTask();
}
mTaskFinishedListener.onFinished(callerLogString);
- mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, mStatus));
+ mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, status));
mBackupManagerService.getWakelock().release();
}
@@ -642,17 +603,12 @@
}
@GuardedBy("mQueueLock")
- private void triggerTransportInitializationLocked() {
- try {
- IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
- mBackupManagerService.getPendingInits().add(transport.name());
- deletePmStateFile();
- mBackupManagerService.backupNow();
- } catch (Exception e) {
- mReporter.onPendingInitializeTransportError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
+ private void triggerTransportInitializationLocked() throws Exception {
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
+ mBackupManagerService.getPendingInits().add(transport.name());
+ deletePmStateFile();
+ mBackupManagerService.backupNow();
}
/** Removes PM state, triggering initialization in the next key-value task. */
@@ -660,35 +616,69 @@
new File(mStateDirectory, PM_PACKAGE).delete();
}
+ /** Same as {@link #extractAgentData(PackageInfo)}, but only for PM package. */
+ private void extractPmAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
+ Preconditions.checkArgument(packageInfo.packageName.equals(PM_PACKAGE));
+ BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
+ mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
+ extractAgentData(packageInfo, mAgent);
+ }
+
/**
- * Returns a {@link Pair}. The first of the pair contains the status. In case the status is
- * {@link BackupTransport#TRANSPORT_OK}, the second of the pair contains the agent result,
- * otherwise {@code null}.
+ * Binds to the agent and extracts its backup data. If this method returns, the data in {@code
+ * mBackupData} is ready to be sent to the transport, otherwise it will throw.
+ *
+ * <p>This method leaves agent resources (agent binder, files and file-descriptors) opened that
+ * need to be cleaned up after terminating, either successfully or exceptionally. This clean-up
+ * can be done with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link
+ * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to
+ * the transport or not. It's the caller responsibility to do the clean-up or delegate it.
*/
- private Pair<Integer, RemoteResult> extractAgentData(String packageName, IBackupAgent agent) {
+ private void extractAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
+ mBackupManagerService.setWorkSource(new WorkSource(packageInfo.applicationInfo.uid));
+ try {
+ mAgent = bindAgent(packageInfo);
+ extractAgentData(packageInfo, mAgent);
+ } finally {
+ mBackupManagerService.setWorkSource(null);
+ }
+ }
+
+ /**
+ * Calls agent {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
+ * ParcelFileDescriptor, long, IBackupCallback, int)} and waits for the result. If this method
+ * returns, the data in {@code mBackupData} is ready to be sent to the transport, otherwise it
+ * will throw.
+ *
+ * <p>This method creates files and file-descriptors for the agent that need to be deleted and
+ * closed after terminating, either successfully or exceptionally. This clean-up can be done
+ * with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link
+ * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to
+ * the transport or not. It's the caller responsibility to do the clean-up or delegate it.
+ */
+ private void extractAgentData(PackageInfo packageInfo, IBackupAgent agent)
+ throws AgentException, TaskException {
+ String packageName = packageInfo.packageName;
mReporter.onExtractAgentData(packageName);
- File blankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
mSavedStateFile = new File(mStateDirectory, packageName);
mBackupDataFile = new File(mDataDirectory, packageName + STAGING_FILE_SUFFIX);
mNewStateFile = new File(mStateDirectory, packageName + NEW_STATE_FILE_SUFFIX);
mReporter.onAgentFilesReady(mBackupDataFile);
- mSavedState = null;
- mBackupData = null;
- mNewState = null;
-
boolean callingAgent = false;
final RemoteResult agentResult;
try {
- File savedStateFileForAgent = (mNonIncremental) ? blankStateFile : mSavedStateFile;
+ File savedStateFileForAgent = (mNonIncremental) ? mBlankStateFile : mSavedStateFile;
// MODE_CREATE to make an empty file if necessary
- mSavedState = ParcelFileDescriptor.open(
- savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE);
- mBackupData = ParcelFileDescriptor.open(
- mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
- mNewState = ParcelFileDescriptor.open(
- mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+ mSavedState =
+ ParcelFileDescriptor.open(savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE);
+ mBackupData =
+ ParcelFileDescriptor.open(
+ mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+ mNewState =
+ ParcelFileDescriptor.open(
+ mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
if (!SELinux.restorecon(mBackupDataFile)) {
mReporter.onRestoreconFailed(mBackupDataFile);
@@ -713,15 +703,40 @@
"doBackup()");
} catch (Exception e) {
mReporter.onCallAgentDoBackupError(packageName, callingAgent, e);
- cleanUpAgentForAgentError();
- // TODO: Remove the check on callingAgent when RemoteCall supports local agent calls.
- int status =
- callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR;
- return Pair.create(status, null);
+ if (callingAgent) {
+ throw AgentException.transitory(e);
+ } else {
+ throw TaskException.create();
+ }
+ } finally {
+ mBlankStateFile.delete();
}
- blankStateFile.delete();
+ checkAgentResult(packageInfo, agentResult);
+ }
- return Pair.create(BackupTransport.TRANSPORT_OK, agentResult);
+ private void checkAgentResult(PackageInfo packageInfo, RemoteResult result)
+ throws AgentException, TaskException {
+ if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
+ // Not an explicit cancel, we need to flag it.
+ mCancelled = true;
+ mReporter.onAgentCancelled(packageInfo);
+ throw TaskException.create();
+ }
+ if (result == RemoteResult.FAILED_CANCELLED) {
+ mReporter.onAgentCancelled(packageInfo);
+ throw TaskException.create();
+ }
+ if (result == RemoteResult.FAILED_TIMED_OUT) {
+ mReporter.onAgentTimedOut(packageInfo);
+ throw AgentException.transitory();
+ }
+ Preconditions.checkState(result.isPresent());
+ long resultCode = result.get();
+ if (resultCode == BackupAgent.RESULT_ERROR) {
+ mReporter.onAgentResultError(packageInfo);
+ throw AgentException.transitory();
+ }
+ Preconditions.checkState(resultCode == BackupAgent.RESULT_SUCCESS);
}
private void agentFail(IBackupAgent agent, String message) {
@@ -801,94 +816,79 @@
}
}
- /** Returns whether to consume next queue package. */
- private boolean sendDataToTransport() {
+ /** Returns transport status. */
+ private int sendDataToTransport() throws AgentException, TaskException, IOException {
Preconditions.checkState(mBackupData != null);
+ checkBackupData(mCurrentPackage.applicationInfo, mBackupDataFile);
String packageName = mCurrentPackage.packageName;
- ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo;
-
- boolean writingWidgetData = false;
- try {
- if (!validateBackupData(applicationInfo, mBackupDataFile)) {
- cleanUpAgentForAgentError();
- return true;
- }
- writingWidgetData = true;
- writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
- } catch (IOException e) {
- if (writingWidgetData) {
- mReporter.onWriteWidgetDataError(packageName, e);
- } else {
- mReporter.onReadAgentDataError(packageName, e);
- }
- cleanUpAgentForAgentError();
- revertTask();
- return false;
- }
+ writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
boolean nonIncremental = mSavedStateFile.length() == 0;
- long size = mBackupDataFile.length();
- if (size > 0) {
- try (ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(mBackupDataFile, MODE_READ_ONLY)) {
- IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.sendDataToTransport()");
- mReporter.onSendDataToTransport(packageName);
- int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
-
- mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
- if (mStatus == BackupTransport.TRANSPORT_OK) {
- mStatus = transport.finishBackup();
- }
- } catch (Exception e) {
- mReporter.onPackageBackupError(packageName, e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
- } else {
- mReporter.onEmptyData(mCurrentPackage);
- mStatus = BackupTransport.TRANSPORT_OK;
- }
-
- if (nonIncremental
- && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
- mReporter.onNonIncrementalAndNonIncrementalRequired();
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
-
-
- boolean processQueue = handleTransportStatus(mStatus, packageName, size);
- // We might report quota exceeded to the agent in handleTransportStatus() above, so we
- // only clean-up after it.
- cleanUpAgentForTransportStatus(mStatus);
- return processQueue;
+ int status = transportPerformBackup(mCurrentPackage, mBackupDataFile, nonIncremental);
+ handleTransportStatus(status, packageName, mBackupDataFile.length());
+ return status;
}
- /** Returns whether to consume next queue package. */
- private boolean handleTransportStatus(int status, String packageName, long size) {
+ private int transportPerformBackup(
+ PackageInfo packageInfo, File backupDataFile, boolean nonIncremental)
+ throws TaskException {
+ String packageName = packageInfo.packageName;
+ long size = backupDataFile.length();
+ if (size <= 0) {
+ mReporter.onEmptyData(packageInfo);
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ int status;
+ try (ParcelFileDescriptor backupData =
+ ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow("KVBT.transportPerformBackup()");
+ mReporter.onTransportPerformBackup(packageName);
+ int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
+
+ status = transport.performBackup(packageInfo, backupData, flags);
+ if (status == BackupTransport.TRANSPORT_OK) {
+ status = transport.finishBackup();
+ }
+ } catch (Exception e) {
+ mReporter.onPackageBackupTransportError(packageName, e);
+ throw TaskException.causedBy(e);
+ }
+
+ if (nonIncremental && status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+ mReporter.onPackageBackupNonIncrementalAndNonIncrementalRequired(packageName);
+ throw TaskException.create();
+ }
+
+ return status;
+ }
+
+ private void handleTransportStatus(int status, String packageName, long size)
+ throws TaskException, AgentException {
if (status == BackupTransport.TRANSPORT_OK) {
mReporter.onPackageBackupComplete(packageName, size);
- return true;
- }
- if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
- mReporter.onPackageBackupRejected(packageName);
- return true;
+ return;
}
if (status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
mReporter.onPackageBackupNonIncrementalRequired(mCurrentPackage);
// Immediately retry the current package.
mQueue.add(0, packageName);
- return true;
+ return;
+ }
+ if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ mReporter.onPackageBackupRejected(packageName);
+ throw AgentException.permanent();
}
if (status == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
mReporter.onPackageBackupQuotaExceeded(packageName);
agentDoQuotaExceeded(mAgent, packageName, size);
- return true;
+ throw AgentException.permanent();
}
// Any other error here indicates a transport-level failure.
mReporter.onPackageBackupTransportFailure(packageName);
- revertTask();
- return false;
+ throw TaskException.forStatus(status);
}
private void agentDoQuotaExceeded(@Nullable IBackupAgent agent, String packageName, long size) {
@@ -908,19 +908,17 @@
}
/**
- * For system apps and pseudo-apps always return {@code true}. For regular apps returns whether
- * {@code backupDataFile} doesn't have any protected keys.
- *
- * <p>If the app has attempted to write any protected keys we also crash them.
+ * For system apps and pseudo-apps never throws. For regular apps throws {@link AgentException}
+ * if {@code backupDataFile} has any protected keys, also crashing the app.
*/
- private boolean validateBackupData(
- @Nullable ApplicationInfo applicationInfo, File backupDataFile) throws IOException {
+ private void checkBackupData(@Nullable ApplicationInfo applicationInfo, File backupDataFile)
+ throws IOException, AgentException {
if (applicationInfo == null || (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
// System apps and pseudo-apps can write what they want.
- return true;
+ return;
}
try (ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
+ ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
BackupDataInput backupDataInput = new BackupDataInput(backupData.getFileDescriptor());
while (backupDataInput.readNextHeader()) {
String key = backupDataInput.getKey();
@@ -928,12 +926,11 @@
mReporter.onAgentIllegalKey(mCurrentPackage, key);
// Crash them if they wrote any protected keys.
agentFail(mAgent, "Illegal backup key: " + key);
- return false;
+ throw AgentException.permanent();
}
backupDataInput.skipEntityData();
}
}
- return true;
}
private int getPerformBackupFlags(boolean userInitiated, boolean nonIncremental) {
@@ -1009,44 +1006,39 @@
}
}
- /** Cleans-up after having called the agent. */
+ /**
+ * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} for exceptional
+ * case.
+ *
+ * <p>Note: Declaring exception parameter so that the caller only calls this when an exception
+ * is thrown.
+ */
+ private void cleanUpAgentForError(BackupException exception) {
+ cleanUpAgent(StateTransaction.DISCARD_NEW);
+ }
+
+ /**
+ * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} according to
+ * transport status returned in {@link #sendDataToTransport(PackageInfo)}.
+ */
private void cleanUpAgentForTransportStatus(int status) {
- updateFiles(status);
- cleanUpAgent();
- }
-
- /** Cleans-up if we failed to call the agent. */
- private void cleanUpAgentForAgentError() {
- mBackupDataFile.delete();
- mNewStateFile.delete();
- cleanUpAgent();
- }
-
- private void updateFiles(int status) {
switch (status) {
case BackupTransport.TRANSPORT_OK:
- mBackupDataFile.delete();
- mNewStateFile.renameTo(mSavedStateFile);
+ cleanUpAgent(StateTransaction.COMMIT_NEW);
break;
case BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED:
- mSavedStateFile.delete();
- mBackupDataFile.delete();
- mNewStateFile.delete();
+ cleanUpAgent(StateTransaction.DISCARD_ALL);
break;
default:
- // Includes:
- // * BackupTransport.TRANSPORT_PACKAGE_REJECTED
- // * BackupTransport.TRANSPORT_QUOTA_EXCEEDED
- // * BackupTransport.TRANSPORT_ERROR
- mBackupDataFile.delete();
- mNewStateFile.delete();
- break;
+ // All other transport statuses are properly converted to agent or task exceptions.
+ throw new AssertionError();
}
}
- /** Cleans-up file-descriptors and unbinds agent. */
- private void cleanUpAgent() {
- mAgent = null;
+ private void cleanUpAgent(@StateTransaction int stateTransaction) {
+ applyStateTransaction(stateTransaction);
+ mBackupDataFile.delete();
+ mBlankStateFile.delete();
tryCloseFileDescriptor(mSavedState, "old state");
tryCloseFileDescriptor(mBackupData, "backup data");
tryCloseFileDescriptor(mNewState, "new state");
@@ -1058,6 +1050,24 @@
if (mCurrentPackage.applicationInfo != null) {
mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
}
+ mAgent = null;
+ }
+
+ private void applyStateTransaction(@StateTransaction int stateTransaction) {
+ switch (stateTransaction) {
+ case StateTransaction.COMMIT_NEW:
+ mNewStateFile.renameTo(mSavedStateFile);
+ break;
+ case StateTransaction.DISCARD_NEW:
+ mNewStateFile.delete();
+ break;
+ case StateTransaction.DISCARD_ALL:
+ mSavedStateFile.delete();
+ mNewStateFile.delete();
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown state transaction " + stateTransaction);
+ }
}
private void tryCloseFileDescriptor(@Nullable Closeable closeable, String logName) {
@@ -1079,4 +1089,16 @@
mPendingCall = null;
return result;
}
+
+ @IntDef({
+ StateTransaction.COMMIT_NEW,
+ StateTransaction.DISCARD_NEW,
+ StateTransaction.DISCARD_ALL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface StateTransaction {
+ int COMMIT_NEW = 0;
+ int DISCARD_NEW = 1;
+ int DISCARD_ALL = 2;
+ }
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/TaskException.java b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java
new file mode 100644
index 0000000..08d2895
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/TaskException.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.backup.keyvalue;
+
+import android.app.backup.BackupTransport;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * The key-value backup task has failed, no more packages will be processed and we shouldn't attempt
+ * any more backups now. These can be caused by transport failures (as opposed to agent failures).
+ *
+ * @see KeyValueBackupTask
+ * @see AgentException
+ */
+class TaskException extends BackupException {
+ private static final int DEFAULT_STATUS = BackupTransport.TRANSPORT_ERROR;
+
+ static TaskException stateCompromised() {
+ return new TaskException(/* stateCompromised */ true, DEFAULT_STATUS);
+ }
+
+ static TaskException stateCompromised(Exception cause) {
+ if (cause instanceof TaskException) {
+ TaskException exception = (TaskException) cause;
+ return new TaskException(cause, /* stateCompromised */ true, exception.getStatus());
+ }
+ return new TaskException(cause, /* stateCompromised */ true, DEFAULT_STATUS);
+ }
+
+ static TaskException forStatus(int status) {
+ Preconditions.checkArgument(
+ status != BackupTransport.TRANSPORT_OK, "Exception based on TRANSPORT_OK");
+ return new TaskException(/* stateCompromised */ false, status);
+ }
+
+ static TaskException causedBy(Exception cause) {
+ if (cause instanceof TaskException) {
+ return (TaskException) cause;
+ }
+ return new TaskException(cause, /* stateCompromised */ false, DEFAULT_STATUS);
+ }
+
+ static TaskException create() {
+ return new TaskException(/* stateCompromised */ false, DEFAULT_STATUS);
+ }
+
+ private final boolean mStateCompromised;
+ private final int mStatus;
+
+ private TaskException(Exception cause, boolean stateCompromised, int status) {
+ super(cause);
+ mStateCompromised = stateCompromised;
+ mStatus = status;
+ }
+
+ private TaskException(boolean stateCompromised, int status) {
+ mStateCompromised = stateCompromised;
+ mStatus = status;
+ }
+
+ boolean isStateCompromised() {
+ return mStateCompromised;
+ }
+
+ int getStatus() {
+ return mStatus;
+ }
+}
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 9af952d..c933833 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -105,7 +105,7 @@
try {
IBackupTransport transport =
transportClient.connectOrThrow(
- "AppBackupUtils.appIsEligibleForBackupAtRuntime");
+ "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
return transport.isAppEligibleForBackup(
packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo));
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 998e441..c2aec29 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -16,6 +16,15 @@
package com.android.server;
+import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
+import static android.app.AppOpsManager.UID_STATE_CACHED;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.UID_STATE_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
+import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.app.AppOpsManager._NUM_UID_STATE;
+
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -98,15 +107,6 @@
import java.util.List;
import java.util.Map;
-import static android.app.AppOpsManager._NUM_UID_STATE;
-import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
-import static android.app.AppOpsManager.UID_STATE_CACHED;
-import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
-import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
-import static android.app.AppOpsManager.UID_STATE_LAST_NON_RESTRICTED;
-import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
-import static android.app.AppOpsManager.UID_STATE_TOP;
-
public class AppOpsService extends IAppOpsService.Stub {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
@@ -1016,7 +1016,7 @@
scheduleWriteLocked();
}
} else {
- if (uidState.opModes.get(code) == mode) {
+ if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
return;
}
if (mode == defaultMode) {
@@ -1971,6 +1971,7 @@
continue;
}
boolean doAllPackages = uidState.opModes != null
+ && uidState.opModes.indexOfKey(code) >= 0
&& uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND;
if (uidState.pkgOps != null) {
for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7602090..a1989e5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -49,10 +52,12 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
+import android.net.INetd;
import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
@@ -75,7 +80,6 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
-import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.UidRange;
@@ -83,7 +87,9 @@
import android.net.VpnService;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
+import android.net.netlink.InetDiagMessage;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -153,7 +159,6 @@
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Tethering;
@@ -256,7 +261,8 @@
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
- private INetworkManagementService mNetd;
+ private INetworkManagementService mNMS;
+ private INetd mNetd;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -756,7 +762,7 @@
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
mContext = checkNotNull(context, "missing Context");
- mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+ mNMS = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mPolicyManagerInternal = checkNotNull(
@@ -764,6 +770,7 @@
"missing NetworkPolicyManagerInternal");
mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
+ mNetd = NetdService.getInstance();
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -846,7 +853,7 @@
mTethering = makeTethering();
- mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+ mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
//set up the listener for user state for creating user VPNs
IntentFilter intentFilter = new IntentFilter();
@@ -861,8 +868,8 @@
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
try {
- mNetd.registerObserver(mTethering);
- mNetd.registerObserver(mDataActivityObserver);
+ mNMS.registerObserver(mTethering);
+ mNMS.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -893,7 +900,7 @@
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
- mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+ mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
registerPrivateDnsSettingsCallbacks();
}
@@ -909,7 +916,7 @@
return mDefaultRequest;
}
};
- return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+ return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
deps);
}
@@ -1473,6 +1480,20 @@
};
/**
+ * Ensures that the system cannot call a particular method.
+ */
+ private boolean disallowedBecauseSystemCaller() {
+ // TODO: start throwing a SecurityException when GnssLocationProvider stops calling
+ // requestRouteToHost.
+ if (isSystem(Binder.getCallingUid())) {
+ log("This method exists only for app backwards compatibility"
+ + " and must not be called by system services.");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
* @param networkType the type of the network over which traffic to the
@@ -1483,6 +1504,9 @@
*/
@Override
public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+ if (disallowedBecauseSystemCaller()) {
+ return false;
+ }
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
@@ -1560,7 +1584,7 @@
if (DBG) log("Adding legacy route " + bestRoute +
" for UID/PID " + uid + "/" + Binder.getCallingPid());
try {
- mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+ mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
} catch (Exception e) {
// never crash - catch them all
if (DBG) loge("Exception trying to add a route: " + e);
@@ -1680,6 +1704,11 @@
"ConnectivityService");
}
+ private boolean checkNetworkStackPermission() {
+ return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_STACK);
+ }
+
private void enforceConnectivityRestrictedNetworksPermission() {
try {
mContext.enforceCallingOrSelfPermission(
@@ -1845,7 +1874,7 @@
if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
try {
- mNetd.addIdleTimer(iface, timeout, type);
+ mNMS.addIdleTimer(iface, timeout, type);
} catch (Exception e) {
// You shall not crash!
loge("Exception in setupDataActivityTracking " + e);
@@ -1864,7 +1893,7 @@
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
try {
// the call fails silently if no idle timer setup for this interface
- mNetd.removeIdleTimer(iface);
+ mNMS.removeIdleTimer(iface);
} catch (Exception e) {
loge("Exception in removeDataActivityTracking " + e);
}
@@ -1872,6 +1901,18 @@
}
/**
+ * Update data activity tracking when network state is updated.
+ */
+ private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+ NetworkAgentInfo oldNetwork) {
+ if (newNetwork != null) {
+ setupDataActivityTracking(newNetwork);
+ }
+ if (oldNetwork != null) {
+ removeDataActivityTracking(oldNetwork);
+ }
+ }
+ /**
* Reads the network specific MTU size from resources.
* and set it on it's iface.
*/
@@ -1899,7 +1940,7 @@
try {
if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
- mNetd.setMtu(iface, mtu);
+ mNMS.setMtu(iface, mtu);
} catch (Exception e) {
Slog.e(TAG, "exception in setMtu()" + e);
}
@@ -2553,7 +2594,7 @@
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- removeDataActivityTracking(nai);
+ updateDataActivityTracking(null /* newNetwork */, nai);
notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.name());
}
@@ -2573,7 +2614,7 @@
// NetworkFactories, so network traffic isn't interrupted for an unnecessarily
// long time.
try {
- mNetd.removeNetwork(nai.network.netId);
+ mNMS.removeNetwork(nai.network.netId);
} catch (Exception e) {
loge("Exception removing network: " + e);
}
@@ -2771,20 +2812,6 @@
}
}
- // TODO: remove this code once we know that the Slog.wtf is never hit.
- //
- // Find all networks that are satisfying this request and remove the request
- // from their request lists.
- // TODO - it's my understanding that for a request there is only a single
- // network satisfying it, so this loop is wasteful
- for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
- if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
- Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
- otherNai.name() + ", but mNetworkAgentInfos says " +
- (nai != null ? nai.name() : "null"));
- }
- }
-
// Maintain the illusion. When this request arrived, we might have pretended
// that a network connected to serve it, even though the network was already
// connected. Now that this request has gone away, we might have to pretend
@@ -3752,7 +3779,7 @@
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
- setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
} else {
setLockdownTracker(null);
}
@@ -4007,7 +4034,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -4624,7 +4651,7 @@
mDnsManager.updatePrivateDnsStatus(netId, newLp);
// Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNetd);
+ networkAgent.updateClat(mNMS);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4663,9 +4690,9 @@
final String prefix = "iface:" + iface;
try {
if (add) {
- mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+ mNetd.wakeupAddInterface(iface, prefix, mark, mask);
} else {
- mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+ mNetd.wakeupDelInterface(iface, prefix, mark, mask);
}
} catch (Exception e) {
loge("Exception modifying wakeup packet monitoring: " + e);
@@ -4681,7 +4708,7 @@
for (String iface : interfaceDiff.added) {
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
- mNetd.addInterfaceToNetwork(iface, netId);
+ mNMS.addInterfaceToNetwork(iface, netId);
wakeupModifyInterface(iface, caps, true);
} catch (Exception e) {
loge("Exception adding interface: " + e);
@@ -4691,7 +4718,7 @@
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, caps, false);
- mNetd.removeInterfaceFromNetwork(iface, netId);
+ mNMS.removeInterfaceFromNetwork(iface, netId);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
@@ -4715,7 +4742,7 @@
if (route.hasGateway()) continue;
if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNetd.addRoute(netId, route);
+ mNMS.addRoute(netId, route);
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for non-gateway: " + e);
@@ -4726,7 +4753,7 @@
if (route.hasGateway() == false) continue;
if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNetd.addRoute(netId, route);
+ mNMS.addRoute(netId, route);
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for gateway: " + e);
@@ -4737,7 +4764,7 @@
for (RouteInfo route : routeDiff.removed) {
if (VDBG) log("Removing Route [" + route + "] from network " + netId);
try {
- mNetd.removeRoute(netId, route);
+ mNMS.removeRoute(netId, route);
} catch (Exception e) {
loge("Exception in removeRoute: " + e);
}
@@ -4849,7 +4876,7 @@
final String newPermission = getNetworkPermission(newNc);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try {
- mNetd.setNetworkPermission(nai.network.netId, newPermission);
+ mNMS.setNetworkPermission(nai.network.netId, newPermission);
} catch (RemoteException e) {
loge("Exception in setNetworkPermission: " + e);
}
@@ -4909,12 +4936,12 @@
if (!newRanges.isEmpty()) {
final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
newRanges.toArray(addedRangesArray);
- mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+ mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray);
}
if (!prevRanges.isEmpty()) {
final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
prevRanges.toArray(removedRangesArray);
- mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+ mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray);
}
} catch (Exception e) {
// Never crash!
@@ -5083,9 +5110,9 @@
private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
- setupDataActivityTracking(newNetwork);
+
try {
- mNetd.setDefaultNetId(newNetwork.network.netId);
+ mNMS.setDefaultNetId(newNetwork.network.netId);
} catch (Exception e) {
loge("Exception setting default network :" + e);
}
@@ -5258,6 +5285,7 @@
}
}
if (isNewDefault) {
+ updateDataActivityTracking(newNetwork, oldDefaultNetwork);
// Notify system services that this network is up.
makeDefault(newNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
@@ -5480,12 +5508,12 @@
try {
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
- mNetd.createVirtualNetwork(networkAgent.network.netId,
+ mNMS.createVirtualNetwork(networkAgent.network.netId,
!networkAgent.linkProperties.getDnsServers().isEmpty(),
(networkAgent.networkMisc == null ||
!networkAgent.networkMisc.allowBypass));
} else {
- mNetd.createPhysicalNetwork(networkAgent.network.netId,
+ mNMS.createPhysicalNetwork(networkAgent.network.netId,
getNetworkPermission(networkAgent.networkCapabilities));
}
} catch (Exception e) {
@@ -5922,4 +5950,49 @@
pw.println(" Get airplane mode.");
}
}
+
+ /**
+ * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
+ * for testing.
+ */
+ private Vpn enforceActiveVpnOrNetworkStackPermission() {
+ if (checkNetworkStackPermission()) {
+ return null;
+ }
+ final int uid = Binder.getCallingUid();
+ final int user = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(user);
+ try {
+ if (vpn.getVpnInfo().ownerUid == uid) return vpn;
+ } catch (NullPointerException e) {
+ /* vpn is null, or VPN is not connected and getVpnInfo() is null. */
+ }
+ }
+ throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
+ + "permission");
+ }
+
+ /**
+ * @param connectionInfo the connection to resolve.
+ * @return {@code uid} if the connection is found and the app has permission to observe it
+ * (e.g., if it is associated with the calling VPN app's tunnel) or {@code INVALID_UID} if the
+ * connection is not found.
+ */
+ public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
+ final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
+ if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
+ throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
+ }
+
+ final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+ connectionInfo.local, connectionInfo.remote);
+
+ /* Filter out Uids not associated with the VPN. */
+ if (vpn != null && !vpn.appliesToUid(uid)) {
+ return INVALID_UID;
+ }
+
+ return uid;
+ }
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 380f6a7..a69d416 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1490,23 +1490,19 @@
}
}
- private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+ private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
private void enforceTunnelPermissions(String callingPackage) {
checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
- if (false) { // STOPSHIP if this line is present
- switch (getAppOpsManager().noteOp(
- TUNNEL_OP,
- Binder.getCallingUid(), callingPackage)) {
- case AppOpsManager.MODE_DEFAULT:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
- break;
- case AppOpsManager.MODE_ALLOWED:
- return;
- default:
- throw new SecurityException("Request to ignore AppOps for non-legacy API");
- }
+ switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+ break;
+ case AppOpsManager.MODE_ALLOWED:
+ return;
+ default:
+ throw new SecurityException("Request to ignore AppOps for non-legacy API");
}
}
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index ee01d86..96ce6a4 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -93,10 +93,12 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
List<LooperStats.ExportedEntry> entries = mStats.getEntries();
entries.sort(Comparator
- .comparing((LooperStats.ExportedEntry entry) -> entry.threadName)
+ .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
+ .thenComparing(entry -> entry.threadName)
.thenComparing(entry -> entry.handlerClassName)
.thenComparing(entry -> entry.messageName));
String header = String.join(",", Arrays.asList(
+ "work_source_uid",
"thread_name",
"handler_class",
"message_name",
@@ -110,11 +112,11 @@
"exception_count"));
pw.println(header);
for (LooperStats.ExportedEntry entry : entries) {
- pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName,
- entry.handlerClassName, entry.messageName, entry.isInteractive,
- entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros,
- entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros,
- entry.exceptionCount);
+ pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.workSourceUid,
+ entry.threadName, entry.handlerClassName, entry.messageName,
+ entry.isInteractive, entry.messageCount, entry.recordedMessageCount,
+ entry.totalLatencyMicros, entry.maxLatencyMicros, entry.cpuUsageMicros,
+ entry.maxCpuUsageMicros, entry.exceptionCount);
}
}
@@ -127,7 +129,12 @@
}
private void setSamplingInterval(int samplingInterval) {
- mStats.setSamplingInterval(samplingInterval);
+ if (samplingInterval > 0) {
+ mStats.setSamplingInterval(samplingInterval);
+ } else {
+ Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
+ + samplingInterval);
+ }
}
/**
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1d163ee..de930f7 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -161,6 +161,8 @@
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
/**
* Name representing {@link #setGlobalAlert(long)} limit when delivered to
* {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -1234,18 +1236,12 @@
@Override
public void startTethering(String[] dhcpRange) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // cmd is "tether start first_start first_stop second_start second_stop ..."
// an odd number of addrs will fail
- final Command cmd = new Command("tether", "start");
- for (String d : dhcpRange) {
- cmd.appendArg(d);
- }
-
try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherStart(dhcpRange);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1253,9 +1249,9 @@
public void stopTethering() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("tether", "stop");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherStop();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1263,25 +1259,21 @@
public boolean isTetheringStarted() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final NativeDaemonEvent event;
try {
- event = mConnector.execute("tether", "status");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ final boolean isEnabled = mNetdService.tetherIsEnabled();
+ return isEnabled;
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
-
- // 210 Tethering services started
- event.checkCode(TetherStatusResult);
- return event.getMessage().endsWith("started");
}
@Override
public void tetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("tether", "interface", "add", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherInterfaceAdd(iface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
List<RouteInfo> routes = new ArrayList<>();
// The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
@@ -1294,9 +1286,9 @@
public void untetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("tether", "interface", "remove", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherInterfaceRemove(iface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
} finally {
removeInterfaceFromLocalNetwork(iface);
}
@@ -1306,11 +1298,10 @@
public String[] listTetheredInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("tether", "interface", "list"),
- TetherInterfaceListResult);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ final List<String> result = mNetdService.tetherInterfaceList();
+ return result.toArray(EMPTY_STRING_ARRAY);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1319,16 +1310,11 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
- final Command cmd = new Command("tether", "dns", "set", netId);
-
- for (String s : dns) {
- cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
- }
try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherDnsSet(netId, dns);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1336,10 +1322,10 @@
public String[] getDnsForwarders() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ final List<String> result = mNetdService.tetherDnsList();
+ return result.toArray(EMPTY_STRING_ARRAY);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d505a77..21f54dd 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -329,6 +329,12 @@
@GuardedBy("mPackagesLock")
private final SparseArray<String> mSandboxIds = new SparseArray<>();
+ /**
+ * List of volumes visible to any user.
+ * TODO: may be have a map of userId -> volumes?
+ */
+ private final CopyOnWriteArrayList<VolumeInfo> mVisibleVols = new CopyOnWriteArrayList<>();
+
private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
/** Holding lock for AppFuse business */
@@ -623,16 +629,12 @@
Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
break;
}
- try {
- mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- }
+ mount(vol);
break;
}
case H_VOLUME_UNMOUNT: {
final VolumeInfo vol = (VolumeInfo) msg.obj;
- unmount(vol.getId());
+ unmount(vol);
break;
}
case H_VOLUME_BROADCAST: {
@@ -869,6 +871,8 @@
addInternalVolumeLocked();
}
+ mVisibleVols.clear();
+
try {
mVold.reset();
@@ -1466,7 +1470,7 @@
= mContext.getPackageManager().getInstalledApplicationsAsUser(
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+ final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
for (int i = appInfos.size() - 1; i >= 0; --i) {
if (appInfos.get(i).isInstantApp()) {
continue;
@@ -1523,7 +1527,7 @@
}
@GuardedBy("mPackagesLock")
- private ArraySet<String> getPackagesForUserPL(int userId) {
+ private ArraySet<String> getAvailablePackagesForUserPL(int userId) {
ArraySet<String> userPackages = mPackages.get(userId);
if (userPackages == null) {
userPackages = new ArraySet<>();
@@ -1535,8 +1539,24 @@
private String[] getPackagesArrayForUser(int userId) {
if (!ENABLE_ISOLATED_STORAGE) return EmptyArray.STRING;
+ final ArraySet<String> userPackages;
synchronized (mPackagesLock) {
- return getPackagesForUserPL(userId).toArray(new String[0]);
+ userPackages = getAvailablePackagesForUserPL(userId);
+ if (!userPackages.isEmpty()) {
+ return userPackages.toArray(new String[0]);
+ }
+ }
+ final List<ApplicationInfo> appInfos =
+ mContext.getPackageManager().getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ synchronized (mPackagesLock) {
+ for (int i = appInfos.size() - 1; i >= 0; --i) {
+ if (appInfos.get(i).isInstantApp()) {
+ continue;
+ }
+ userPackages.add(appInfos.get(i).packageName);
+ }
+ return userPackages.toArray(new String[0]);
}
}
@@ -1747,8 +1767,15 @@
if (isMountDisallowed(vol)) {
throw new SecurityException("Mounting " + volId + " restricted by policy");
}
+ mount(vol);
+ }
+
+ private void mount(VolumeInfo vol) {
try {
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
+ if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
+ mVisibleVols.add(vol);
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1759,8 +1786,15 @@
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
+ unmount(vol);
+ }
+
+ private void unmount(VolumeInfo vol) {
try {
mVold.unmount(vol.id);
+ if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
+ mVisibleVols.remove(vol);
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3596,6 +3630,14 @@
pw.decreaseIndent();
pw.println();
+ pw.println("mVisibleVols:");
+ pw.increaseIndent();
+ for (int i = 0; i < mVisibleVols.size(); i++) {
+ mVisibleVols.get(i).dump(pw);
+ }
+ pw.decreaseIndent();
+
+ pw.println();
pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
final Pair<String, Long> pair = StorageManager.getPrimaryStoragePathAndSize();
if (pair == null) {
@@ -3716,7 +3758,7 @@
int userId) {
final String sandboxId;
synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+ final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
// If userPackages is empty, it means the user is not started yet, so no need to
// do anything now.
if (userPackages.isEmpty() || userPackages.contains(packageName)) {
@@ -3734,5 +3776,29 @@
Slog.wtf(TAG, e);
}
}
+
+ @Override
+ public String[] getVisibleVolumesForUser(int userId) {
+ final ArrayList<String> visibleVolsForUser = new ArrayList<>();
+ for (int i = mVisibleVols.size() - 1; i >= 0; --i) {
+ final VolumeInfo vol = mVisibleVols.get(i);
+ if (vol.isVisibleForUser(userId)) {
+ visibleVolsForUser.add(getVolumeLabel(vol));
+ }
+ }
+ return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]);
+ }
+
+ private String getVolumeLabel(VolumeInfo vol) {
+ // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold
+ switch (vol.getType()) {
+ case VolumeInfo.TYPE_EMULATED:
+ return "emulated";
+ case VolumeInfo.TYPE_PUBLIC:
+ return vol.fsUuid == null ? vol.id : vol.fsUuid;
+ default:
+ return null;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 98b88cb..fb8894b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -213,6 +213,8 @@
private PhoneCapability mPhoneCapability = null;
+ private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
private final LocalLog mLocalLog = new LocalLog(100);
private PreciseDataConnectionState mPreciseDataConnectionState =
@@ -752,6 +754,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) {
+ try {
+ r.callback.onPreferredDataSubIdChanged(mPreferredDataSubId);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -1573,6 +1582,31 @@
}
}
+ public void notifyPreferredDataSubIdChanged(int preferredSubId) {
+ if (!checkNotifyPermission("notifyPreferredDataSubIdChanged()")) {
+ return;
+ }
+
+ if (VDBG) {
+ log("notifyPreferredDataSubIdChanged: preferredSubId=" + preferredSubId);
+ }
+
+ synchronized (mRecords) {
+ mPreferredDataSubId = preferredSubId;
+
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE)) {
+ try {
+ r.callback.onPreferredDataSubIdChanged(preferredSubId);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -1610,6 +1644,7 @@
pw.println("mBackgroundCallState=" + mBackgroundCallState);
pw.println("mVoLteServiceState=" + mVoLteServiceState);
pw.println("mPhoneCapability=" + mPhoneCapability);
+ pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
pw.decreaseIndent();
@@ -1647,6 +1682,7 @@
intent.putExtras(data);
// Pass the subscription along with the intent.
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -1701,6 +1737,7 @@
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
}
// If the phoneId is invalid, the broadcast is for overall call state.
if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/services/core/java/com/android/server/UiModeManagerInternal.java
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
copy to services/core/java/com/android/server/UiModeManagerInternal.java
index c50d6d6..ef29069 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
+++ b/services/core/java/com/android/server/UiModeManagerInternal.java
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -11,15 +11,17 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.stackdivider.events;
-
-import com.android.systemui.recents.events.EventBus;
+package com.android.server;
/**
- * Sent when the divider isn't draging anymore.
+ * UiModeManager local system service interface.
+ *
+ * @hide Only for use within the system server.
*/
-public class StoppedDragingEvent extends EventBus.Event {
+public abstract class UiModeManagerInternal {
+
+ public abstract boolean isNightMode();
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index cb03255..5538e72 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -42,31 +42,25 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
import android.util.Slog;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-
import com.android.internal.R;
import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
-import com.android.server.power.ShutdownThread;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
final class UiModeManagerService extends SystemService {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
@@ -114,6 +108,8 @@
private PowerManager.WakeLock mWakeLock;
+ private final LocalService mLocalService = new LocalService();
+
public UiModeManagerService(Context context) {
super(context);
}
@@ -248,6 +244,7 @@
}, TAG + ".onStart");
publishBinderService(Context.UI_MODE_SERVICE, mService);
+ publishLocalService(UiModeManagerInternal.class, mLocalService);
}
private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
@@ -373,7 +370,8 @@
pw.println("Current UI Mode Service state:");
pw.print(" mDockState="); pw.print(mDockState);
pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
- pw.print(" mNightMode="); pw.print(mNightMode);
+ pw.print(" mNightMode="); pw.print(mNightMode); pw.print(" (");
+ pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
pw.print(" mNightModeLocked="); pw.print(mNightModeLocked);
pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
@@ -466,7 +464,7 @@
uiMode |= mNightMode << 4;
}
- if (mPowerSave && !mNightModeLocked) {
+ if (mPowerSave) {
uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
uiMode |= Configuration.UI_MODE_NIGHT_YES;
}
@@ -845,4 +843,22 @@
}
}
}
+
+ public final class LocalService extends UiModeManagerInternal {
+
+ @Override
+ public boolean isNightMode() {
+ synchronized (mLock) {
+ final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0;
+ if (LOG) {
+ Slog.d(TAG,
+ "LocalService.isNightMode(): mNightMode=" + mNightMode
+ + "; mComputedNightMode=" + mComputedNightMode
+ + "; uiMode=" + mConfiguration.uiMode
+ + "; isIt=" + isIt);
+ }
+ return isIt;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 6d69fcd..0b836f0 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -94,7 +94,7 @@
"media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"com.android.bluetooth", // Bluetooth service
- "statsd", // Stats daemon
+ "/system/bin/statsd", // Stats daemon
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e5aa3f4..461d39d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -629,7 +629,7 @@
return false;
}
- IIntentSender target = mAm.getIntentSenderLocked(
+ IIntentSender target = mAm.mPendingIntentController.getIntentSender(
ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
callingUid, userId, null, null, 0, new Intent[]{service},
new String[]{service.resolveType(mAm.mContext.getContentResolver())},
@@ -2490,7 +2490,7 @@
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
- mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
+ mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
r.postNotification();
created = true;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 01421c7..fab967c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -890,6 +890,8 @@
mRemoved = true;
releaseSelfIfNeeded();
+
+ mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
}
private void releaseSelfIfNeeded() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7533db1..aa14da0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -134,7 +134,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -369,7 +368,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
-import java.lang.ref.WeakReference;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -417,7 +415,6 @@
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
- private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
// Mock "pretend we're idle now" broadcast action to the job scheduler; declared
// here so that while the job scheduler can depend on AMS, the other way around
@@ -563,6 +560,7 @@
String mDeviceOwnerName;
final UserController mUserController;
+ final PendingIntentController mPendingIntentController;
final AppErrors mAppErrors;
@@ -665,11 +663,6 @@
int mNextIsolatedProcessUid = 0;
/**
- * The currently running heavy-weight process, if any.
- */
- ProcessRecord mHeavyWeightProcess = null;
-
- /**
* Non-persistent appId whitelist for background restrictions
*/
int[] mBackgroundAppIdWhitelist = new int[] {
@@ -826,18 +819,6 @@
final SparseArray<UidRecord> mValidateUids = new SparseArray<>();
/**
- * Packages that the user has asked to have run in screen size
- * compatibility mode instead of filling the screen.
- */
- final CompatModePackages mCompatModePackages;
-
- /**
- * Set of IntentSenderRecord objects that are currently active.
- */
- final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
- = new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
-
- /**
* Fingerprints (hashCode()) of stack traces that we've
* already logged DropBox entries for. Guarded by itself. If
* something (rogue user app) forces this over
@@ -1097,11 +1078,10 @@
volatile boolean mSystemReady = false;
volatile boolean mOnBattery = false;
volatile int mFactoryTest;
+ volatile boolean mBooting = false;
- @GuardedBy("this") boolean mBooting = false;
@GuardedBy("this") boolean mCallFinishBooting = false;
@GuardedBy("this") boolean mBootAnimationComplete = false;
- @GuardedBy("this") boolean mLaunchWarningShown = false;
private @GuardedBy("this") boolean mCheckedForSetup = false;
final Context mContext;
@@ -1388,10 +1368,8 @@
long mLastWriteTime = 0;
- /**
- * Set to true after the system has finished booting.
- */
- boolean mBooted = false;
+ /** Set to true after the system has finished booting. */
+ volatile boolean mBooted = false;
/**
* Current boot phase.
@@ -1440,9 +1418,6 @@
static final int UPDATE_TIME_ZONE = 13;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int KILL_APPLICATION_MSG = 22;
- static final int FINALIZE_PENDING_INTENT_MSG = 23;
- static final int POST_HEAVY_NOTIFICATION_MSG = 24;
- static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26;
static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27;
static final int CLEAR_DNS_CACHE_MSG = 28;
@@ -1451,7 +1426,6 @@
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
- static final int FINISH_BOOTING_MSG = 45;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
@@ -1462,7 +1436,6 @@
static final int IDLE_UIDS_MSG = 58;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
- static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
@@ -1661,21 +1634,6 @@
mServices.serviceForegroundCrash(
(ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY));
} break;
- case DISPATCH_PENDING_INTENT_CANCEL_MSG: {
- RemoteCallbackList<IResultReceiver> callbacks
- = (RemoteCallbackList<IResultReceiver>)msg.obj;
- int N = callbacks.beginBroadcast();
- for (int i = 0; i < N; i++) {
- try {
- callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
- } catch (RemoteException e) {
- }
- }
- callbacks.finishBroadcast();
- // We have to clean up the RemoteCallbackList here, because otherwise it will
- // needlessly hold the enclosed callbacks until the remote process dies.
- callbacks.kill();
- } break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -1755,68 +1713,6 @@
false, userId, reason);
}
} break;
- case FINALIZE_PENDING_INTENT_MSG: {
- ((PendingIntentRecord)msg.obj).completeFinalize();
- } break;
- case POST_HEAVY_NOTIFICATION_MSG: {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
- return;
- }
-
- ActivityRecord root = (ActivityRecord)msg.obj;
- final WindowProcessController process = root.app;
- if (process == null) {
- return;
- }
-
- try {
- Context context = mContext.createPackageContext(process.mInfo.packageName, 0);
- String text = mContext.getString(R.string.heavy_weight_notification,
- context.getApplicationInfo().loadLabel(context.getPackageManager()));
- Notification notification =
- new Notification.Builder(context,
- SystemNotificationChannels.HEAVY_WEIGHT_APP)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setWhen(0)
- .setOngoing(true)
- .setTicker(text)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(text)
- .setContentText(
- mContext.getText(R.string.heavy_weight_notification_detail))
- .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
- root.intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
- new UserHandle(root.userId)))
- .build();
- try {
- inm.enqueueNotificationWithTag("android", "android", null,
- SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION,
- notification, root.userId);
- } catch (RuntimeException e) {
- Slog.w(ActivityManagerService.TAG,
- "Error showing notification for heavy-weight app", e);
- } catch (RemoteException e) {
- }
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Unable to create context for heavy notification", e);
- }
- } break;
- case CANCEL_HEAVY_NOTIFICATION_MSG: {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
- return;
- }
- try {
- inm.cancelNotificationWithTag("android", null,
- SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
- } catch (RuntimeException e) {
- Slog.w(ActivityManagerService.TAG,
- "Error canceling notification for service", e);
- } catch (RemoteException e) {
- }
- } break;
case CHECK_EXCESSIVE_POWER_USE_MSG: {
synchronized (ActivityManagerService.this) {
checkExcessivePowerUsageLocked();
@@ -1853,17 +1749,6 @@
}
break;
}
- case FINISH_BOOTING_MSG: {
- if (msg.arg1 != 0) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
- finishBooting();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- if (msg.arg2 != 0) {
- mAtmInternal.enableScreenAfterBoot(mBooted);
- }
- break;
- }
case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
try {
Locale l = (Locale) msg.obj;
@@ -2430,7 +2315,6 @@
mAppErrors = null;
mAppOpsService = mInjector.getAppOpsService(null, null);
mBatteryStatsService = null;
- mCompatModePackages = null;
mConstants = null;
mHandler = null;
mHandlerThread = null;
@@ -2442,6 +2326,7 @@
mSystemThread = null;
mUiHandler = injector.getUiHandler(null);
mUserController = null;
+ mPendingIntentController = null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
@@ -2495,7 +2380,8 @@
final File systemDir = SystemServiceManager.ensureSystemDir();
// TODO: Move creation of battery stats service outside of activity manager service.
- mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
+ mBatteryStatsService = new BatteryStatsService(systemContext, systemDir,
+ BackgroundThread.get().getHandler());
mBatteryStatsService.getActiveStatistics().readLocked();
mBatteryStatsService.scheduleWriteToDisk();
mOnBattery = DEBUG_POWER ? true
@@ -2511,6 +2397,9 @@
mUserController = new UserController(this);
+ mPendingIntentController = new PendingIntentController(
+ mHandlerThread.getLooper(), mUserController);
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -2519,7 +2408,6 @@
}
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
- mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityTaskManager = atm;
@@ -2597,6 +2485,7 @@
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
mActivityTaskManager.onActivityManagerInternalAdded();
mUgmInternal.onActivityManagerInternalAdded();
+ mPendingIntentController.onActivityManagerInternalAdded();
// Wait for the synchronized block started in mProcessCpuThread,
// so that any other access to mProcessCpuTracker from main thread
// will be blocked during mProcessCpuTracker initialization.
@@ -3648,6 +3537,9 @@
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
try {
+ final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
+ .getVisibleVolumesForUser(UserHandle.getUserId(uid));
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
@@ -3657,12 +3549,14 @@
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
+ packageNames, visibleVolIds,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName,
+ packageNames, visibleVolIds,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
checkTime(startTime, "startProcess: returned from zygote!");
@@ -3791,31 +3685,31 @@
return true;
}
- void updateUsageStats(ActivityRecord component, boolean resumed) {
+ void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
- "updateUsageStats: comp=" + component + "res=" + resumed);
+ "updateUsageStats: comp=" + activity + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- component.app.mUid, component.realActivity.getPackageName(),
- component.realActivity.getShortClassName(), resumed ?
+ uid, activity.getPackageName(),
+ activity.getShortClassName(), resumed ?
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(component.realActivity, component.userId,
+ mUsageStatsService.reportEvent(activity, userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);
}
synchronized (stats) {
- stats.noteActivityResumedLocked(component.app.mUid);
+ stats.noteActivityResumedLocked(uid);
}
} else {
if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(component.realActivity, component.userId,
+ mUsageStatsService.reportEvent(activity, userId,
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
- stats.noteActivityPausedLocked(component.app.mUid);
+ stats.noteActivityPausedLocked(uid);
}
}
}
@@ -3895,8 +3789,8 @@
mCheckedForSetup = checked;
}
- CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
- return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
+ CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
+ return mAtmInternal.compatibilityInfoForPackage(ai);
}
private void enforceNotIsolatedCaller(String caller) {
@@ -3906,37 +3800,8 @@
}
@Override
- public int getPackageScreenCompatMode(String packageName) {
- enforceNotIsolatedCaller("getPackageScreenCompatMode");
- synchronized (this) {
- return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
- }
- }
-
- @Override
public void setPackageScreenCompatMode(String packageName, int mode) {
- enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
- "setPackageScreenCompatMode");
- synchronized (this) {
- mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
- }
- }
-
- @Override
- public boolean getPackageAskScreenCompat(String packageName) {
- enforceNotIsolatedCaller("getPackageAskScreenCompat");
- synchronized (this) {
- return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
- }
- }
-
- @Override
- public void setPackageAskScreenCompat(String packageName, boolean ask) {
- enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
- "setPackageAskScreenCompat");
- synchronized (this) {
- mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
- }
+ mActivityTaskManager.setPackageScreenCompatMode(packageName, mode);
}
private boolean hasUsageStatsPermission(String callingPackage) {
@@ -4307,19 +4172,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
- synchronized(this) {
- final ProcessRecord proc = mHeavyWeightProcess;
- if (proc == null) {
- return;
- }
-
- proc.getWindowProcessController().finishActivities();
-
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- proc.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.finishHeavyWeightApp();
}
@Override
@@ -4349,7 +4202,6 @@
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
- final boolean clearLaunchStartTime = !restarting && app.removed && app.foregroundActivities;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
false /*replacingPid*/);
if (!kept && !restarting) {
@@ -4390,18 +4242,6 @@
mWindowManager.continueSurfaceLayout();
}
- // TODO (b/67683350)
- // When an app process is removed, activities from the process may be relaunched. In the
- // case of forceStopPackageLocked the activities are finished before any window is drawn,
- // and the launch time is not cleared. This will be incorrectly used to calculate launch
- // time for the next launched activity launched in the same windowing mode.
- if (clearLaunchStartTime) {
- final LaunchTimeTracker.Entry entry = mStackSupervisor
- .getLaunchTimeTracker().getEntry(mStackSupervisor.getWindowingMode());
- if (entry != null) {
- entry.mLaunchStartTime = 0;
- }
- }
}
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
@@ -5641,55 +5481,8 @@
}
if (packageName == null || uninstalling) {
- // Remove pending intents. For now we only do this when force
- // stopping users, because we have some problems when doing this
- // for packages -- app widgets are not currently cleaned up for
- // such packages, so they can be left with bad pending intents.
- if (mIntentSenderRecords.size() > 0) {
- Iterator<WeakReference<PendingIntentRecord>> it
- = mIntentSenderRecords.values().iterator();
- while (it.hasNext()) {
- WeakReference<PendingIntentRecord> wpir = it.next();
- if (wpir == null) {
- it.remove();
- continue;
- }
- PendingIntentRecord pir = wpir.get();
- if (pir == null) {
- it.remove();
- continue;
- }
- if (packageName == null) {
- // Stopping user, remove all objects for the user.
- if (pir.key.userId != userId) {
- // Not the same user, skip it.
- continue;
- }
- } else {
- if (UserHandle.getAppId(pir.uid) != appId) {
- // Different app id, skip it.
- continue;
- }
- if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
- // Different user, skip it.
- continue;
- }
- if (!pir.key.packageName.equals(packageName)) {
- // Different package, skip it.
- continue;
- }
- }
- if (!doit) {
- return true;
- }
- didSomething = true;
- it.remove();
- makeIntentSenderCanceledLocked(pir);
- if (pir.key.activity != null && pir.key.activity.pendingResults != null) {
- pir.key.activity.pendingResults.remove(pir.ref);
- }
- }
- }
+ didSomething |= mPendingIntentController.removePendingIntentsForPackage(
+ packageName, userId, appId, doit);
}
if (doit) {
@@ -5789,11 +5582,8 @@
return false;
}
removeProcessNameLocked(name, uid);
- if (mHeavyWeightProcess == app) {
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+
boolean needRestart = false;
if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
int pid = app.pid;
@@ -5851,11 +5641,7 @@
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
removeProcessNameLocked(app.processName, app.uid);
- if (mHeavyWeightProcess == app) {
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
cleanupAppInLaunchingProvidersLocked(app, true);
@@ -6032,9 +5818,10 @@
PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
- + processName + " with config " + getGlobalConfiguration());
+ + processName + " with config "
+ + app.getWindowProcessController().getConfiguration());
ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
- app.compat = compatibilityInfoForPackageLocked(appInfo);
+ app.compat = compatibilityInfoForPackage(appInfo);
ProfilerInfo profilerInfo = null;
String preBindAgent = null;
@@ -6145,8 +5932,8 @@
instr2.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
- new Configuration(getGlobalConfiguration()), app.compat,
- getCommonServicesLocked(app.isolated),
+ new Configuration(app.getWindowProcessController().getConfiguration()),
+ app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
} else {
@@ -6154,8 +5941,8 @@
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
- new Configuration(getGlobalConfiguration()), app.compat,
- getCommonServicesLocked(app.isolated),
+ new Configuration(app.getWindowProcessController().getConfiguration()),
+ app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
}
@@ -6231,7 +6018,7 @@
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
- compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
+ compatibilityInfoForPackage(mBackupTarget.appInfo),
mBackupTarget.backupMode);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
@@ -6264,11 +6051,6 @@
}
}
- void postFinishBooting(boolean finishBooting, boolean enableScreen) {
- mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
- finishBooting ? 1 : 0, enableScreen ? 1 : 0));
- }
-
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != myUid()) {
@@ -6278,6 +6060,8 @@
}
final void finishBooting() {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
+
synchronized (this) {
if (!mBootAnimationComplete) {
mCallFinishBooting = true;
@@ -6383,6 +6167,8 @@
});
mUserController.scheduleStartProfiles();
}
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@Override
@@ -6393,9 +6179,7 @@
mBootAnimationComplete = true;
}
if (callFinishBooting) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -6410,9 +6194,7 @@
}
if (booting) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (enableScreen) {
@@ -6483,90 +6265,19 @@
}
}
- return getIntentSenderLocked(type, packageName, callingUid, userId,
- token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
-
+ if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+ return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
+ token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+ }
+ return mPendingIntentController.getIntentSender(type, packageName, callingUid,
+ userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+ bOptions);
} catch (RemoteException e) {
throw new SecurityException(e);
}
}
}
- IIntentSender getIntentSenderLocked(int type, String packageName,
- int callingUid, int userId, IBinder token, String resultWho,
- int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
- Bundle bOptions) {
- if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
- ActivityRecord activity = null;
- if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
- activity = ActivityRecord.isInStackLocked(token);
- if (activity == null) {
- Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
- return null;
- }
- if (activity.finishing) {
- Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
- return null;
- }
- }
-
- // We're going to be splicing together extras before sending, so we're
- // okay poking into any contained extras.
- if (intents != null) {
- for (int i = 0; i < intents.length; i++) {
- intents[i].setDefusable(true);
- }
- }
- Bundle.setDefusable(bOptions, true);
-
- final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
- final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
- final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
- flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
- |PendingIntent.FLAG_UPDATE_CURRENT);
-
- PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, activity,
- resultWho, requestCode, intents, resolvedTypes, flags,
- SafeActivityOptions.fromBundle(bOptions), userId);
- WeakReference<PendingIntentRecord> ref;
- ref = mIntentSenderRecords.get(key);
- PendingIntentRecord rec = ref != null ? ref.get() : null;
- if (rec != null) {
- if (!cancelCurrent) {
- if (updateCurrent) {
- if (rec.key.requestIntent != null) {
- rec.key.requestIntent.replaceExtras(intents != null ?
- intents[intents.length - 1] : null);
- }
- if (intents != null) {
- intents[intents.length-1] = rec.key.requestIntent;
- rec.key.allIntents = intents;
- rec.key.allResolvedTypes = resolvedTypes;
- } else {
- rec.key.allIntents = null;
- rec.key.allResolvedTypes = null;
- }
- }
- return rec;
- }
- makeIntentSenderCanceledLocked(rec);
- mIntentSenderRecords.remove(key);
- }
- if (noCreate) {
- return rec;
- }
- rec = new PendingIntentRecord(this, key, callingUid);
- mIntentSenderRecords.put(key, rec.ref);
- if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
- if (activity.pendingResults == null) {
- activity.pendingResults
- = new HashSet<WeakReference<PendingIntentRecord>>();
- }
- activity.pendingResults.add(rec.ref);
- }
- return rec;
- }
-
@Override
public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
Intent intent, String resolvedType,
@@ -6606,44 +6317,7 @@
@Override
public void cancelIntentSender(IIntentSender sender) {
- if (!(sender instanceof PendingIntentRecord)) {
- return;
- }
- synchronized(this) {
- PendingIntentRecord rec = (PendingIntentRecord)sender;
- try {
- final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
- if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
- String msg = "Permission Denial: cancelIntentSender() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " is not allowed to cancel package "
- + rec.key.packageName;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- } catch (RemoteException e) {
- throw new SecurityException(e);
- }
- cancelIntentSenderLocked(rec, true);
- }
- }
-
- void cancelIntentSenderLocked(PendingIntentRecord rec, boolean cleanActivity) {
- makeIntentSenderCanceledLocked(rec);
- mIntentSenderRecords.remove(rec.key);
- if (cleanActivity && rec.key.activity != null) {
- rec.key.activity.pendingResults.remove(rec.ref);
- }
- }
-
- void makeIntentSenderCanceledLocked(PendingIntentRecord rec) {
- rec.canceled = true;
- RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
- if (callbacks != null) {
- mHandler.obtainMessage(DISPATCH_PENDING_INTENT_CANCEL_MSG, callbacks).sendToTarget();
- }
+ mPendingIntentController.cancelIntentSender(sender);
}
@Override
@@ -7954,7 +7628,23 @@
}
}
- boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
+ boolean providerRunning = false;
+
+ if (cpr != null && cpr.proc != null) {
+ providerRunning = !cpr.proc.killed;
+
+ // Note if killedByAm is also set, this means the provider process has just been
+ // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
+ // yet. So we need to call appDiedLocked() here and let it clean up.
+ // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
+ // how to test this case.)
+ if (cpr.proc.killed && cpr.proc.killedByAm) {
+ checkTime(startTime, "getContentProviderImpl: before appDied (killedByAm)");
+ appDiedLocked(cpr.proc);
+ checkTime(startTime, "getContentProviderImpl: after appDied (killedByAm)");
+ }
+ }
+
if (providerRunning) {
cpi = cpr.info;
String msg;
@@ -8406,10 +8096,17 @@
}
}
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
+ @Deprecated
+ @Override
public void removeContentProviderExternal(String name, IBinder token) {
+ removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
- int userId = UserHandle.getCallingUserId();
long ident = Binder.clearCallingIdentity();
try {
removeContentProviderExternalUnchecked(name, token, userId);
@@ -8853,7 +8550,7 @@
StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
- final ProcessRecord r = new ProcessRecord(this, info, proc, uid);
+ final ProcessRecord r = new ProcessRecord(this, info, proc, uid, getGlobalConfiguration());
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
@@ -9741,9 +9438,10 @@
mBatteryStatsService.noteWakupAlarm(sourcePkg, sourceUid, workSource, tag);
if (workSource != null) {
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag);
+ StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag, sourcePkg);
} else {
- StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid, null, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid, null, tag,
+ sourcePkg);
}
}
@@ -10845,7 +10543,7 @@
int clientTargetSdk) {
outInfo.pid = app.pid;
outInfo.uid = app.info.uid;
- if (mHeavyWeightProcess == app) {
+ if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
}
if (app.isPersistent()) {
@@ -11000,7 +10698,7 @@
pw.println("-------------------------------------------------------------------------------");
}
- dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -11293,7 +10991,7 @@
opti++;
}
synchronized (this) {
- dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage);
+ mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage);
}
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
if (opti < args.length) {
@@ -11888,13 +11586,13 @@
TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
- if (mHeavyWeightProcess != null && (dumpPackage == null
- || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
+ if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
- pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
}
if (dumpAll && mPendingStarts.size() > 0) {
if (needSep) pw.println();
@@ -11913,10 +11611,10 @@
pw.println(" mConfigWillChange: "
+ mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
}
- if (mCompatModePackages.getPackages().size() > 0) {
+ if (mActivityTaskManager.mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
for (Map.Entry<String, Integer> entry
- : mCompatModePackages.getPackages().entrySet()) {
+ : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage != null && !dumpPackage.equals(pkg)) {
@@ -12298,12 +11996,13 @@
proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
}
- if (mHeavyWeightProcess != null && (dumpPackage == null
- || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
- mHeavyWeightProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
+ if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+ ((ProcessRecord) mActivityTaskManager.mHeavyWeightProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
}
- for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) {
+ for (Map.Entry<String, Integer> entry
+ : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage == null || dumpPackage.equals(pkg)) {
@@ -12556,8 +12255,8 @@
pw.println();
pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
- if (mHeavyWeightProcess != null) {
- pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ if (mActivityTaskManager.mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
}
return true;
@@ -12990,61 +12689,6 @@
mUgmInternal.dump(pw, dumpAll, dumpPackage);
}
- void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, String dumpPackage) {
- boolean printed = false;
-
- pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
-
- if (mIntentSenderRecords.size() > 0) {
- // Organize these by package name, so they are easier to read.
- final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>();
- final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>();
- final Iterator<WeakReference<PendingIntentRecord>> it
- = mIntentSenderRecords.values().iterator();
- while (it.hasNext()) {
- WeakReference<PendingIntentRecord> ref = it.next();
- PendingIntentRecord rec = ref != null ? ref.get() : null;
- if (rec == null) {
- weakRefs.add(ref);
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
- continue;
- }
- ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName);
- if (list == null) {
- list = new ArrayList<>();
- byPackage.put(rec.key.packageName, list);
- }
- list.add(rec);
- }
- for (int i = 0; i < byPackage.size(); i++) {
- ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i);
- printed = true;
- pw.print(" * "); pw.print(byPackage.keyAt(i));
- pw.print(": "); pw.print(intents.size()); pw.println(" items");
- for (int j = 0; j < intents.size(); j++) {
- pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
- if (dumpAll) {
- intents.get(j).dump(pw, " ");
- }
- }
- }
- if (weakRefs.size() > 0) {
- printed = true;
- pw.println(" * WEAK REFS:");
- for (int i = 0; i < weakRefs.size(); i++) {
- pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
- }
- }
- }
-
- if (!printed) {
- pw.println(" (nothing)");
- }
- }
-
private static final int dumpProcessList(PrintWriter pw,
ActivityManagerService service, List list,
String prefix, String normalLabel, String persistentLabel,
@@ -15189,11 +14833,7 @@
if (!replacingPid) {
removeProcessNameLocked(app.processName, app.uid, app);
}
- if (mHeavyWeightProcess == app) {
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
@@ -15323,24 +14963,6 @@
}
}
- ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
- boolean fgRequired, String callingPackage, int userId)
- throws TransactionTooLargeException {
- synchronized(this) {
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
- "startServiceInPackage: " + service + " type=" + resolvedType);
- final long origId = Binder.clearCallingIdentity();
- ComponentName res;
- try {
- res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid, fgRequired, callingPackage, userId);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- return res;
- }
- }
-
@Override
public int stopService(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
@@ -15577,7 +15199,7 @@
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
try {
proc.thread.scheduleCreateBackupAgent(app,
- compatibilityInfoForPackageLocked(app), backupMode);
+ compatibilityInfoForPackage(app), backupMode);
} catch (RemoteException e) {
// Will time out on the backup manager side
}
@@ -15676,7 +15298,7 @@
if (proc.thread != null) {
try {
proc.thread.scheduleDestroyBackupAgent(appInfo,
- compatibilityInfoForPackageLocked(appInfo));
+ compatibilityInfoForPackage(appInfo));
} catch (Exception e) {
Slog.e(TAG, "Exception when unbinding backup agent:");
e.printStackTrace();
@@ -16385,7 +16007,6 @@
mServices.forceStopPackageLocked(ssp, userId);
mAtmInternal.onPackageUninstalled(ssp);
- mCompatModePackages.handlePackageUninstalledLocked(ssp);
mBatteryStatsService.notePackageUninstalled(ssp);
}
} else {
@@ -16447,7 +16068,7 @@
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
final boolean replacing =
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- mCompatModePackages.handlePackageAddedLocked(ssp, replacing);
+ mAtmInternal.onPackageAdded(ssp, replacing);
try {
ApplicationInfo ai = AppGlobals.getPackageManager().
@@ -16464,7 +16085,6 @@
Uri data = intent.getData();
String ssp;
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
- mCompatModePackages.handlePackageDataClearedLocked(ssp);
mAtmInternal.onPackageDataCleared(ssp);
}
break;
@@ -17222,30 +16842,6 @@
}
}
- // =========================================================
- // CONFIGURATION
- // =========================================================
-
- public ConfigurationInfo getDeviceConfigurationInfo() {
- ConfigurationInfo config = new ConfigurationInfo();
- synchronized (this) {
- final Configuration globalConfig = getGlobalConfiguration();
- config.reqTouchScreen = globalConfig.touchscreen;
- config.reqKeyboardType = globalConfig.keyboard;
- config.reqNavigation = globalConfig.navigation;
- if (globalConfig.navigation == Configuration.NAVIGATION_DPAD
- || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) {
- config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
- }
- if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED
- && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
- config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
- }
- config.reqGlEsVersion = GL_ES_VERSION;
- }
- return config;
- }
-
@Override
public StackInfo getFocusedStackInfo() throws RemoteException {
return mActivityTaskManager.getFocusedStackInfo();
@@ -17809,7 +17405,7 @@
}
}
- if (app == mHeavyWeightProcess) {
+ if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
@@ -21055,7 +20651,8 @@
memoryStat.pgmajfault,
memoryStat.rssInBytes,
memoryStat.cacheInBytes,
- memoryStat.swapInBytes);
+ memoryStat.swapInBytes,
+ memoryStat.rssHighWatermarkInBytes);
processMemoryStates.add(processMemoryState);
}
}
@@ -21090,18 +20687,6 @@
ActivityManagerService.this.trimApplications();
}
- public int getPackageScreenCompatMode(ApplicationInfo ai) {
- synchronized (ActivityManagerService.this) {
- return mCompatModePackages.computeCompatModeLocked(ai);
- }
- }
-
- public void setPackageScreenCompatMode(ApplicationInfo ai, int mode) {
- synchronized (ActivityManagerService.this) {
- mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
- }
- }
-
public void closeSystemDialogs(String reason) {
ActivityManagerService.this.closeSystemDialogs(reason);
}
@@ -21150,6 +20735,38 @@
}
@Override
+ public void updateCpuStats() {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateCpuStats();
+ }
+ }
+
+ @Override
+ public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed);
+ }
+ }
+
+ @Override
+ public void updateForegroundTimeIfOnBattery(
+ String packageName, int uid, long cpuTimeDiff) {
+ synchronized (ActivityManagerService.this) {
+ if (!mBatteryStatsService.isOnBattery()) {
+ return;
+ }
+ final BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ final BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(uid, packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(cpuTimeDiff);
+ }
+ }
+ }
+ }
+
+ @Override
public void sendForegroundProfileChanged(int userId) {
mUserController.sendForegroundProfileChanged(userId);
}
@@ -21209,6 +20826,71 @@
return ActivityManagerService.this.getTaskForActivity(token, onlyRoot);
}
}
+
+ @Override
+ public void setBooting(boolean booting) {
+ mBooting = booting;
+ }
+
+ @Override
+ public boolean isBooting() {
+ return mBooting;
+ }
+
+ @Override
+ public void setBooted(boolean booted) {
+ mBooted = booted;
+ }
+
+ @Override
+ public boolean isBooted() {
+ return mBooted;
+ }
+
+ @Override
+ public void finishBooting() {
+ ActivityManagerService.this.finishBooting();
+ }
+
+ @Override
+ public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
+ long duration, String tag) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.tempWhitelistForPendingIntentLocked(
+ callerPid, callerUid, targetUid, duration, tag);
+ }
+ }
+
+ @Override
+ public int broadcastIntentInPackage(String packageName, int uid, Intent intent,
+ String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
+ Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
+ boolean sticky, int userId) {
+ synchronized (ActivityManagerService.this) {
+ return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
+ intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
+ requiredPermission, bOptions, serialized, sticky, userId);
+ }
+ }
+
+ @Override
+ public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
+ boolean fgRequired, String callingPackage, int userId)
+ throws TransactionTooLargeException {
+ synchronized(ActivityManagerService.this) {
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
+ "startServiceInPackage: " + service + " type=" + resolvedType);
+ final long origId = Binder.clearCallingIdentity();
+ ComponentName res;
+ try {
+ res = mServices.startServiceLocked(null, service,
+ resolvedType, -1, uid, fgRequired, callingPackage, userId);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return res;
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b24c36a..40c555f8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -568,9 +568,6 @@
if (result.who != null) {
pw.println("Activity: " + result.who.flattenToShortString());
}
- if (result.thisTime >= 0) {
- pw.println("ThisTime: " + result.thisTime);
- }
if (result.totalTime >= 0) {
pw.println("TotalTime: " + result.totalTime);
}
@@ -2063,7 +2060,12 @@
pw.print("has-secure-screen-lock: "); pw.println(kgm.isDeviceSecure());
}
- ConfigurationInfo configInfo = mInternal.getDeviceConfigurationInfo();
+ ConfigurationInfo configInfo = null;
+ try {
+ configInfo = mTaskInterface.getDeviceConfigurationInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
if (protoOutputStream != null) {
protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 263c34f..18cdb05 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -3,13 +3,13 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.processStateAmToProto;
-import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
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 com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS;
@@ -28,9 +28,9 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_PROCESS_NAME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_REAL_ACTIVITY;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME;
@@ -57,16 +57,16 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PENDING_UI_CLEAN;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PROCESS_NAME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_PACKAGE_NAME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_SHORT_COMPONENT_NAME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_PROC_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_WHITELIST_TAG;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
@@ -75,8 +75,10 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
import android.content.Context;
import android.content.Intent;
@@ -88,20 +90,27 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.Trace;
+import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.StatsLog;
+import android.util.TimeUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
-import java.util.ArrayList;
-
/**
- * Handles logging into Tron.
+ * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
+ * determine app launch times and draw delays. Source of truth for activity metrics and provides
+ * data for Tron, logcat, event logs and {@link android.app.WaitResult}.
+ *
+ * Tests:
+ * atest SystemMetricsFunctionalTests
*/
class ActivityMetricsLogger {
@@ -116,6 +125,8 @@
private static final int WINDOW_STATE_INVALID = -1;
private static final long INVALID_START_TIME = -1;
+ private static final int INVALID_DELAY = -1;
+ private static final int INVALID_TRANSITION_TYPE = -1;
private static final int MSG_CHECK_VISIBILITY = 0;
@@ -144,6 +155,8 @@
private final H mHandler;
private ArtManagerInternal mArtManagerInternal;
+ private boolean mDrawingTraceActive;
+ private final StringBuilder mStringBuilder = new StringBuilder();
private final class H extends Handler {
@@ -166,36 +179,56 @@
private ActivityRecord launchedActivity;
private int startResult;
private boolean currentTransitionProcessRunning;
+ /** Elapsed time from when we launch an activity to when its windows are drawn. */
private int windowsDrawnDelayMs;
- private int startingWindowDelayMs = -1;
- private int bindApplicationDelayMs = -1;
+ private int startingWindowDelayMs = INVALID_DELAY;
+ private int bindApplicationDelayMs = INVALID_DELAY;
private int reason = APP_TRANSITION_TIMEOUT;
private boolean loggedWindowsDrawn;
private boolean loggedStartingWindowDrawn;
+ private boolean launchTraceActive;
}
- private final class WindowingModeTransitionInfoSnapshot {
+ final class WindowingModeTransitionInfoSnapshot {
final private ApplicationInfo applicationInfo;
final private WindowProcessController processRecord;
- final private String packageName;
- final private String launchedActivityName;
+ final String packageName;
+ final String launchedActivityName;
final private String launchedActivityLaunchedFromPackage;
final private String launchedActivityLaunchToken;
final private String launchedActivityAppRecordRequiredAbi;
+ final String launchedActivityShortComponentName;
final private String processName;
final private int reason;
final private int startingWindowDelayMs;
final private int bindApplicationDelayMs;
- final private int windowsDrawnDelayMs;
- final private int type;
+ final int windowsDrawnDelayMs;
+ final int type;
+ final int userId;
+ /**
+ * Elapsed time from when we launch an activity to when the app reported it was
+ * fully drawn. If this is not reported then the value is set to INVALID_DELAY.
+ */
+ final int windowsFullyDrawnDelayMs;
+ final int activityRecordIdHashCode;
private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
- applicationInfo = info.launchedActivity.appInfo;
- packageName = info.launchedActivity.packageName;
- launchedActivityName = info.launchedActivity.info.name;
- launchedActivityLaunchedFromPackage = info.launchedActivity.launchedFromPackage;
- launchedActivityLaunchToken = info.launchedActivity.info.launchToken;
- launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null
+ this(info, info.launchedActivity);
+ }
+
+ private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
+ ActivityRecord launchedActivity) {
+ this(info, launchedActivity, INVALID_DELAY);
+ }
+
+ private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
+ ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs) {
+ applicationInfo = launchedActivity.appInfo;
+ packageName = launchedActivity.packageName;
+ launchedActivityName = launchedActivity.info.name;
+ launchedActivityLaunchedFromPackage = launchedActivity.launchedFromPackage;
+ launchedActivityLaunchToken = launchedActivity.info.launchToken;
+ launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
? null
: info.launchedActivity.app.getRequiredAbi();
reason = info.reason;
@@ -205,6 +238,10 @@
type = getTransitionType(info);
processRecord = findProcessForActivity(info.launchedActivity);
processName = info.launchedActivity.processName;
+ userId = launchedActivity.userId;
+ launchedActivityShortComponentName = launchedActivity.shortComponentName;
+ activityRecordIdHashCode = System.identityHashCode(launchedActivity);
+ this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
}
}
@@ -336,7 +373,7 @@
|| windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
// Failed to launch or it was not a process switch, so we don't care about the timing.
- reset(true /* abort */);
+ reset(true /* abort */, info);
return;
} else if (otherWindowModesLaunching) {
// Don't log this windowing mode but continue with the other windowing modes.
@@ -352,6 +389,7 @@
mWindowingModeTransitionInfo.put(windowingMode, newInfo);
mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
+ startTraces(newInfo);
}
/**
@@ -365,18 +403,21 @@
/**
* Notifies the tracker that all windows of the app have been drawn.
*/
- void notifyWindowsDrawn(int windowingMode, long timestamp) {
+ WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(int windowingMode, long timestamp) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
if (info == null || info.loggedWindowsDrawn) {
- return;
+ return null;
}
info.windowsDrawnDelayMs = calculateDelay(timestamp);
info.loggedWindowsDrawn = true;
+ final WindowingModeTransitionInfoSnapshot infoSnapshot =
+ new WindowingModeTransitionInfoSnapshot(info);
if (allWindowsDrawn() && mLoggedTransitionStarting) {
- reset(false /* abort */);
+ reset(false /* abort */, info);
}
+ return infoSnapshot;
}
/**
@@ -395,7 +436,7 @@
* Notifies the tracker that the app transition is starting.
*
* @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
- * of ActivityManagerInternal.APP_TRANSITION_* reasons.
+ * of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
@@ -414,7 +455,7 @@
info.reason = windowingModeToReason.valueAt(index);
}
if (allWindowsDrawn()) {
- reset(false /* abort */);
+ reset(false /* abort */, null /* WindowingModeTransitionInfo */);
}
}
@@ -453,8 +494,9 @@
logAppTransitionCancel(info);
mWindowingModeTransitionInfo.remove(r.getWindowingMode());
if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */);
+ reset(true /* abort */, info);
}
+ stopFullyDrawnTraceIfNeeded();
}
}
}
@@ -489,19 +531,19 @@
&& mWindowingModeTransitionInfo.size() > 0;
}
- private void reset(boolean abort) {
+ private void reset(boolean abort, WindowingModeTransitionInfo info) {
if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort);
if (!abort && isAnyTransitionActive()) {
logAppTransitionMultiEvents();
}
+ stopLaunchTrace(info);
mCurrentTransitionStartTime = INVALID_START_TIME;
- mCurrentTransitionDelayMs = -1;
+ mCurrentTransitionDelayMs = INVALID_DELAY;
mLoggedTransitionStarting = false;
mWindowingModeTransitionInfo.clear();
}
private int calculateCurrentDelay() {
-
// Shouldn't take more than 25 days to launch an app, so int is fine here.
return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
}
@@ -513,7 +555,7 @@
private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
final int type = getTransitionType(info);
- if (type == -1) {
+ if (type == INVALID_TRANSITION_TYPE) {
return;
}
final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED);
@@ -534,7 +576,7 @@
for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index);
final int type = getTransitionType(info);
- if (type == -1) {
+ if (type == INVALID_TRANSITION_TYPE) {
return;
}
@@ -546,6 +588,7 @@
final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
BackgroundThread.getHandler().post(() -> logAppTransition(
currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
+ BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
info.launchedActivity.info.launchToken = null;
}
@@ -572,11 +615,11 @@
currentTransitionDeviceUptime);
builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
builder.setSubtype(info.reason);
- if (info.startingWindowDelayMs != -1) {
+ if (info.startingWindowDelayMs != INVALID_DELAY) {
builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
info.startingWindowDelayMs);
}
- if (info.bindApplicationDelayMs != -1) {
+ if (info.bindApplicationDelayMs != INVALID_DELAY) {
builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
info.bindApplicationDelayMs);
}
@@ -613,6 +656,24 @@
logAppStartMemoryStateCapture(info);
}
+ private void logAppDisplayed(WindowingModeTransitionInfoSnapshot info) {
+ if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
+ return;
+ }
+
+ EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME,
+ info.userId, info.activityRecordIdHashCode, info.launchedActivityShortComponentName,
+ info.windowsDrawnDelayMs);
+
+ StringBuilder sb = mStringBuilder;
+ sb.setLength(0);
+ sb.append("Displayed ");
+ sb.append(info.launchedActivityShortComponentName);
+ sb.append(": ");
+ TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb);
+ Log.i(TAG, sb.toString());
+ }
+
private int convertAppStartTransitionType(int tronType) {
if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
return StatsLog.APP_START_OCCURRED__TYPE__COLD;
@@ -626,11 +687,12 @@
return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
}
- void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) {
+ WindowingModeTransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
+ boolean restoredFromBundle) {
final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
r.getWindowingMode());
if (info == null) {
- return;
+ return null;
}
final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
builder.setPackageName(r.packageName);
@@ -653,6 +715,25 @@
info.launchedActivity.info.name,
info.currentTransitionProcessRunning,
startupTimeMs);
+ stopFullyDrawnTraceIfNeeded();
+ final WindowingModeTransitionInfoSnapshot infoSnapshot =
+ new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
+ BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+ return infoSnapshot;
+ }
+
+ private void logAppFullyDrawn(WindowingModeTransitionInfoSnapshot info) {
+ if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
+ return;
+ }
+
+ StringBuilder sb = mStringBuilder;
+ sb.setLength(0);
+ sb.append("Fully drawn ");
+ sb.append(info.launchedActivityShortComponentName);
+ sb.append(": ");
+ TimeUtils.formatDuration(info.windowsFullyDrawnDelayMs, sb);
+ Log.i(TAG, sb.toString());
}
void logActivityStart(Intent intent, ProcessRecord callerApp, ActivityRecord r,
@@ -754,7 +835,7 @@
} else if (info.startResult == START_SUCCESS) {
return TYPE_TRANSITION_COLD_LAUNCH;
}
- return -1;
+ return INVALID_TRANSITION_TYPE;
}
private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) {
@@ -799,4 +880,46 @@
}
return mArtManagerInternal;
}
+
+ /**
+ * Starts traces for app launch and draw times. We stop the fully drawn trace if its already
+ * active since the app may not have reported fully drawn in the previous launch.
+ *
+ * See {@link android.app.Activity#reportFullyDrawn()}
+ *
+ * @param info
+ * */
+ private void startTraces(WindowingModeTransitionInfo info) {
+ if (info == null) {
+ return;
+ }
+ stopFullyDrawnTraceIfNeeded();
+ int transitionType = getTransitionType(info);
+ if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
+ || transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+ + info.launchedActivity.packageName, 0);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
+ mDrawingTraceActive = true;
+ info.launchTraceActive = true;
+ }
+ }
+
+ private void stopLaunchTrace(WindowingModeTransitionInfo info) {
+ if (info == null) {
+ return;
+ }
+ if (info.launchTraceActive) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+ + info.launchedActivity.packageName, 0);
+ info.launchTraceActive = false;
+ }
+ }
+
+ void stopFullyDrawnTraceIfNeeded() {
+ if (mDrawingTraceActive) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
+ mDrawingTraceActive = false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 628207c..5853ad9 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,20 +17,21 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
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_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_OPEN_CROSS_PROFILE_APPS;
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.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
+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;
@@ -80,7 +81,8 @@
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
@@ -93,6 +95,13 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -103,21 +112,11 @@
import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
-import static com.android.server.am.EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME;
-import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
import static com.android.server.am.TaskPersister.DEBUG;
import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
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;
@@ -132,6 +131,7 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
@@ -143,7 +143,6 @@
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
-import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -163,7 +162,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.service.voice.IVoiceInteractionSession;
@@ -185,6 +183,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
import com.android.server.AttributeCache.Entry;
+import com.android.server.am.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.AppWindowContainerController;
@@ -265,9 +264,6 @@
private int windowFlags; // custom window flags for preview window.
private TaskRecord task; // the task this is in.
private long createTime = System.currentTimeMillis();
- long displayStartTime; // when we started launching this activity
- long fullyDrawnStartTime; // when we started launching this activity
- private long startTime; // last time this activity was started
long lastVisibleTime; // last time this activity became visible
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
long pauseTime; // last time we started pausing the activity
@@ -535,15 +531,6 @@
pw.print("requestedVrComponent=");
pw.println(requestedVrComponent);
}
- if (displayStartTime != 0 || startTime != 0) {
- pw.print(prefix); pw.print("displayStartTime=");
- if (displayStartTime == 0) pw.print("0");
- else TimeUtils.formatDuration(displayStartTime, now, pw);
- pw.print(" startTime=");
- if (startTime == 0) pw.print("0");
- else TimeUtils.formatDuration(startTime, now, pw);
- pw.println();
- }
final boolean waitingVisible =
mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this);
if (lastVisibleTime != 0 || waitingVisible || nowVisible) {
@@ -1279,20 +1266,14 @@
/**
* Check whether this activity can be launched on the specified display.
+ *
* @param displayId Target display id.
- * @return {@code true} if either it is the default display or this activity is resizeable and
- * can be put a secondary screen.
+ * @return {@code true} if either it is the default display or this activity can be put on a
+ * secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
- final TaskRecord task = getTask();
-
- // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
- // This allows for a non resizable activity to be launched into a resizeable task.
- final boolean resizeable =
- task != null ? task.isResizeable() : supportsResizeableMultiWindow();
-
- return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- resizeable, launchedFromPid, launchedFromUid, info);
+ return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+ launchedFromUid, info);
}
/**
@@ -2011,79 +1992,13 @@
}
public void reportFullyDrawnLocked(boolean restoredFromBundle) {
- final long curTime = SystemClock.uptimeMillis();
- if (displayStartTime != 0) {
- reportLaunchTimeLocked(curTime);
+ final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+ .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
+ if (info != null) {
+ mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+ info.windowsFullyDrawnDelayMs);
}
- final LaunchTimeTracker.Entry entry = mStackSupervisor.getLaunchTimeTracker().getEntry(
- getWindowingMode());
- if (fullyDrawnStartTime != 0 && entry != null) {
- final long thisTime = curTime - fullyDrawnStartTime;
- final long totalTime = entry.mFullyDrawnStartTime != 0
- ? (curTime - entry.mFullyDrawnStartTime) : thisTime;
- if (SHOW_ACTIVITY_START_TIME) {
- Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
- EventLog.writeEvent(AM_ACTIVITY_FULLY_DRAWN_TIME,
- userId, System.identityHashCode(this), shortComponentName,
- thisTime, totalTime);
- StringBuilder sb = service.mStringBuilder;
- sb.setLength(0);
- sb.append("Fully drawn ");
- sb.append(shortComponentName);
- sb.append(": ");
- TimeUtils.formatDuration(thisTime, sb);
- if (thisTime != totalTime) {
- sb.append(" (total ");
- TimeUtils.formatDuration(totalTime, sb);
- sb.append(")");
- }
- Log.i(TAG, sb.toString());
- }
- if (totalTime > 0) {
- //service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime);
- }
- entry.mFullyDrawnStartTime = 0;
- }
- mStackSupervisor.getActivityMetricsLogger().logAppTransitionReportedDrawn(this,
- restoredFromBundle);
- fullyDrawnStartTime = 0;
}
-
- private void reportLaunchTimeLocked(final long curTime) {
- final LaunchTimeTracker.Entry entry = mStackSupervisor.getLaunchTimeTracker().getEntry(
- getWindowingMode());
- if (entry == null) {
- return;
- }
- final long thisTime = curTime - displayStartTime;
- final long totalTime = entry.mLaunchStartTime != 0
- ? (curTime - entry.mLaunchStartTime) : thisTime;
- if (SHOW_ACTIVITY_START_TIME) {
- Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0);
- EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME,
- userId, System.identityHashCode(this), shortComponentName,
- thisTime, totalTime);
- StringBuilder sb = service.mStringBuilder;
- sb.setLength(0);
- sb.append("Displayed ");
- sb.append(shortComponentName);
- sb.append(": ");
- TimeUtils.formatDuration(thisTime, sb);
- if (thisTime != totalTime) {
- sb.append(" (total ");
- TimeUtils.formatDuration(totalTime, sb);
- sb.append(")");
- }
- Log.i(TAG, sb.toString());
- }
- mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
- if (totalTime > 0) {
- //service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
- }
- displayStartTime = 0;
- entry.mLaunchStartTime = 0;
- }
-
@Override
public void onStartingWindowDrawn(long timestamp) {
synchronized (service.mGlobalLock) {
@@ -2095,13 +2010,12 @@
@Override
public void onWindowsDrawn(long timestamp) {
synchronized (service.mGlobalLock) {
- mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(),
- timestamp);
- if (displayStartTime != 0) {
- reportLaunchTimeLocked(timestamp);
- }
+ final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+ .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
+ final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+ mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+ windowsDrawnDelayMs);
mStackSupervisor.sendWaitingVisibleReportLocked(this);
- startTime = 0;
finishLaunchTickingLocked();
if (task != null) {
task.hasBeenVisible = true;
@@ -2331,7 +2245,7 @@
}
final CompatibilityInfo compatInfo =
- service.mAm.compatibilityInfoForPackageLocked(info.applicationInfo);
+ service.compatibilityInfoForPackageLocked(info.applicationInfo);
final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
@@ -2495,6 +2409,14 @@
}
}
+ /**
+ * @return {@code true} if this activity was reparented to another display but
+ * {@link #ensureActivityConfiguration} is not called.
+ */
+ boolean shouldUpdateConfigForDisplayChanged() {
+ return mLastReportedDisplayId != getDisplayId();
+ }
+
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
false /* ignoreStopState */);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aa4e68d..29b04cc 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -107,6 +107,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -150,7 +151,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.wm.ConfigurationContainer;
@@ -1317,16 +1318,13 @@
+ " callers=" + Debug.getCallers(5));
r.setState(RESUMED, "minimalResumeActivityLocked");
r.completeResumeLocked();
- mStackSupervisor.getLaunchTimeTracker().setLaunchTime(r);
if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
"Launch completed; removing icicle of " + r.icicle);
}
private void clearLaunchTime(ActivityRecord r) {
// Make sure that there is no activity waiting for this to launch.
- if (mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
- r.displayStartTime = r.fullyDrawnStartTime = 0;
- } else {
+ if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
mStackSupervisor.removeTimeoutsForActivityLocked(r);
mStackSupervisor.scheduleIdleTimeoutLocked(r);
}
@@ -1512,16 +1510,16 @@
prev.getTask().touchActiveTime();
clearLaunchTime(prev);
- mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
+ mStackSupervisor.getActivityMetricsLogger().stopFullyDrawnTraceIfNeeded();
- mService.mAm.updateCpuStats();
+ mService.updateCpuStats();
if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
- mService.mAm.updateUsageStats(prev, false);
+ mService.updateUsageStats(prev, false);
mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
@@ -1683,20 +1681,13 @@
if (prev != null) {
prev.resumeKeyDispatchingLocked();
- if (prev.hasProcess() && prev.cpuTimeAtResume > 0
- && mService.mAm.mBatteryStatsService.isOnBattery()) {
- long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
- if (diff > 0) {
- BatteryStatsImpl bsi = mService.mAm.mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
- }
- }
- }
+ final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
+ if (prev.hasProcess() && prev.cpuTimeAtResume > 0 && diff > 0) {
+ final Runnable r = PooledLambda.obtainRunnable(
+ ActivityManagerInternal::updateForegroundTimeIfOnBattery,
+ mService.mAmInternal, prev.info.packageName, prev.info.applicationInfo.uid,
+ diff);
+ mService.mH.post(r);
}
prev.cpuTimeAtResume = 0; // reset it
}
@@ -1713,7 +1704,7 @@
mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
}
- void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
+ private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
@@ -2407,7 +2398,7 @@
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
- if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
+ if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
return false;
}
@@ -2455,13 +2446,24 @@
if (shouldSleepOrShutDownActivities()
&& mLastPausedActivity == next
&& mStackSupervisor.allPausedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityLocked: Going to sleep and all paused");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return false;
+ // 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;
+ if (!mService.mShuttingDown && !mTopActivityOccludesKeyguard
+ && next.canShowWhenLocked()) {
+ ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
+ nothingToResume = shouldSleepActivities();
+ }
+ if (nothingToResume) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeTopActivityLocked: Going to sleep and all paused");
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return false;
+ }
}
// Make sure that the user who owns this activity is started. If not,
@@ -2691,7 +2693,7 @@
lastStack == null ? null :lastStack.mResumedActivity;
final ActivityState lastState = next.getState();
- mService.mAm.updateCpuStats();
+ mService.updateCpuStats();
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ " (in existing)");
@@ -4149,7 +4151,7 @@
for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
PendingIntentRecord rec = apr.get();
if (rec != null) {
- mService.mAm.cancelIntentSenderLocked(rec, false);
+ mService.mPendingIntentController.cancelIntentSender(rec, false);
}
}
r.pendingResults = null;
@@ -4166,7 +4168,7 @@
mWindowManager.notifyAppRelaunchesCleared(r.appToken);
}
- void removeTimeoutsForActivityLocked(ActivityRecord r) {
+ private void removeTimeoutsForActivityLocked(ActivityRecord r) {
mStackSupervisor.removeTimeoutsForActivityLocked(r);
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
@@ -4368,12 +4370,8 @@
if (hadApp) {
if (removeFromApp) {
r.app.removeActivity(r);
- if (mService.mAm.mHeavyWeightProcess != null
- && mService.mAm.mHeavyWeightProcess.getWindowProcessController() == r.app
- && !r.app.hasActivities()) {
- mService.mAm.mHeavyWeightProcess = null;
- mService.mAm.mHandler.sendEmptyMessage(
- ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
+ if (!r.app.hasActivities()) {
+ mService.clearHeavyWeightProcessIfEquals(r.app);
}
if (!r.app.hasActivities()) {
// Update any services we are bound to that might care about whether
@@ -4564,7 +4562,7 @@
r.getTask().taskId, r.shortComponentName,
"proc died without state saved");
if (r.getState() == RESUMED) {
- mService.mAm.updateUsageStats(r, false);
+ mService.updateUsageStats(r, false);
}
}
} else {
@@ -5375,12 +5373,14 @@
display.positionChildAtTop(this, false /* includingParents */);
}
+ /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
boolean setPause, String reason) {
if (!moveToFront) {
return;
}
+ final ActivityState origState = r.getState();
// If the activity owns the last resumed activity, transfer that together,
// so that we don't resume the same activity again in the new stack.
// Apps may depend on onResume()/onPause() being called in pairs.
@@ -5393,9 +5393,14 @@
mPausingActivity = r;
schedulePauseTimeout(r);
}
- // Move the stack in which we are placing the activity to the front. The call will also
- // make sure the activity focus is set.
+ // Move the stack in which we are placing the activity to the front.
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()) {
+ // TODO(b/111361570): Support multiple focused apps in WM
+ mService.setResumedActivityUncheckLocked(r, reason);
+ }
}
public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 310898e..9688d26 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -25,6 +25,7 @@
import static android.app.ActivityTaskManager.INVALID_STACK_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;
@@ -450,7 +451,6 @@
private boolean mTaskLayersChanged = true;
private ActivityMetricsLogger mActivityMetricsLogger;
- private LaunchTimeTracker mLaunchTimeTracker = new LaunchTimeTracker();
private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
@@ -488,8 +488,8 @@
}
/** Check if placing task or activity on specified display is allowed. */
- boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
- int callingUid, ActivityInfo activityInfo) {
+ boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+ ActivityInfo activityInfo) {
if (displayId == DEFAULT_DISPLAY) {
// No restrictions for the default display.
return true;
@@ -498,10 +498,6 @@
// Can't launch on secondary displays if feature is not supported.
return false;
}
- if (!resizeable && !displayConfigMatchesGlobal(displayId)) {
- // Can't apply wrong configuration to non-resizeable activities.
- return false;
- }
if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
// Can't place activities to a display that has restricted launch rules.
// In this case the request should be made by explicitly adding target display id and
@@ -650,10 +646,6 @@
return mActivityMetricsLogger;
}
- LaunchTimeTracker getLaunchTimeTracker() {
- return mLaunchTimeTracker;
- }
-
public KeyguardController getKeyguardController() {
return mKeyguardController;
}
@@ -690,7 +682,7 @@
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
final Display[] displays = mDisplayManager.getDisplays();
- for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+ 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) {
@@ -804,7 +796,7 @@
}
final ActivityRecord r = topRunningActivityLocked();
- if (mService.mAm.mBooting || !mService.mAm.mBooted) {
+ if (mService.isBooting() || !mService.isBooted()) {
if (r != null && r.idle) {
checkFinishBootingLocked();
}
@@ -836,7 +828,7 @@
}
boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
- if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
+ if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
return false;
}
@@ -1183,8 +1175,8 @@
}
}
- void waitActivityVisible(ComponentName name, WaitResult result) {
- final WaitInfo waitInfo = new WaitInfo(name, result);
+ void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
+ final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
mWaitingForActivityVisible.add(waitInfo);
}
@@ -1215,8 +1207,7 @@
changed = true;
result.timeout = false;
result.who = w.getComponent();
- result.totalTime = SystemClock.uptimeMillis() - result.thisTime;
- result.thisTime = result.totalTime;
+ result.totalTime = SystemClock.uptimeMillis() - w.getStartTime();
mWaitingForActivityVisible.remove(w);
}
}
@@ -1255,8 +1246,7 @@
}
}
- void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
- long thisTime, long totalTime) {
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) {
boolean changed = false;
for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
WaitResult w = mWaitingActivityLaunched.remove(i);
@@ -1266,7 +1256,6 @@
if (r != null) {
w.who = new ComponentName(r.info.packageName, r.info.name);
}
- w.thisTime = thisTime;
w.totalTime = totalTime;
// Do not modify w.result.
}
@@ -1534,7 +1523,7 @@
r.sleeping = false;
r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
- r.compat = mService.mAm.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
ProfilerInfo profilerInfo = null;
if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
@@ -1562,7 +1551,8 @@
// we have to always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
+ app.getWindowProcessController().getConfiguration(),
+ r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
@@ -1595,22 +1585,17 @@
if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mAm.mHasHeavyWeightFeature) {
- // This may be a heavy-weight process! Note that the package
- // manager will ensure that only activity can run in the main
- // process of the .apk, which is the only thing that will be
- // considered heavy-weight.
+ // This may be a heavy-weight process! Note that the package manager will ensure
+ // that only activity can run in the main process of the .apk, which is the only
+ // thing that will be considered heavy-weight.
if (app.processName.equals(app.info.packageName)) {
- if (mService.mAm.mHeavyWeightProcess != null
- && mService.mAm.mHeavyWeightProcess != app) {
- Slog.w(TAG, "Starting new heavy weight process " + app
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != proc) {
+ Slog.w(TAG, "Starting new heavy weight process " + proc
+ " when already running "
- + mService.mAm.mHeavyWeightProcess);
+ + mService.mHeavyWeightProcess);
}
- mService.mAm.mHeavyWeightProcess = app;
- Message msg = mService.mAm.mHandler.obtainMessage(
- ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
- msg.obj = r;
- mService.mAm.mHandler.sendMessage(msg);
+ mService.setHeavyWeightProcess(r);
}
}
@@ -1736,8 +1721,6 @@
ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
- getLaunchTimeTracker().setLaunchTime(r);
-
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
@@ -2057,15 +2040,15 @@
*/
@GuardedBy("mService")
private boolean checkFinishBootingLocked() {
- final boolean booting = mService.mAm.mBooting;
+ final boolean booting = mService.isBooting();
boolean enableScreen = false;
- mService.mAm.mBooting = false;
- if (!mService.mAm.mBooted) {
- mService.mAm.mBooted = true;
+ mService.setBooting(false);
+ if (!mService.isBooted()) {
+ mService.setBooted(true);
enableScreen = true;
}
if (booting || enableScreen) {
- mService.mAm.postFinishBooting(booting, enableScreen);
+ mService.postFinishBooting(booting, enableScreen);
}
return booting;
}
@@ -2090,7 +2073,7 @@
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
+ reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY);
}
// This is a hack to semi-deal with a race condition
@@ -3196,10 +3179,10 @@
// Kill the running processes. Post on handle since we don't want to hold the service lock
// while calling into AM.
- final Runnable r = PooledLambda.obtainRunnable(
+ final Message m = PooledLambda.obtainMessage(
ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
procsToKill);
- mService.mH.post(r);
+ mService.mH.sendMessage(m);
}
/**
@@ -3686,7 +3669,7 @@
final ActivityStack stack = r.getStack();
if (isTopDisplayFocusedStack(stack)) {
- mService.mAm.updateUsageStats(r, true);
+ mService.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -4530,11 +4513,14 @@
mService.setTaskWindowingMode(task.taskId,
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
if (preferredDisplayId != actualDisplayId) {
- // Display a warning toast that we tried to put a non-resizeable task on a secondary
- // display with config different from global config.
+ Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
+ // Display a warning toast that we failed to put a task on a secondary display.
mService.getTaskChangeNotificationController()
.notifyActivityLaunchOnSecondaryDisplayFailed();
return;
+ } else if (!forceNonResizable && handleForcedResizableTask(task,
+ FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
+ return;
}
}
@@ -4554,16 +4540,23 @@
return;
}
+ handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
+ }
+
+ /**
+ * @return {@code true} if the top activity of the task is forced to be resizable and the user
+ * was notified about activity being forced resized.
+ */
+ private boolean handleForcedResizableTask(TaskRecord task, int reason) {
final ActivityRecord topActivity = task.getTopActivity();
if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
- && !topActivity.noDisplay) {
+ && !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
- final int reason = isSecondaryDisplayPreferred
- ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
- : FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
task.taskId, reason, packageName);
+ return true;
}
+ return false;
}
void activityRelaunchedLocked(IBinder token) {
@@ -4938,10 +4931,13 @@
static class WaitInfo {
private final ComponentName mTargetComponent;
private final WaitResult mResult;
+ /** Time stamp when we started to wait for {@link WaitResult}. */
+ private final long mStartTimeMs;
- public WaitInfo(ComponentName targetComponent, WaitResult result) {
+ WaitInfo(ComponentName targetComponent, WaitResult result, long startTimeMs) {
this.mTargetComponent = targetComponent;
this.mResult = result;
+ this.mStartTimeMs = startTimeMs;
}
public boolean matches(ComponentName targetComponent) {
@@ -4952,6 +4948,10 @@
return mResult;
}
+ public long getStartTime() {
+ return mStartTimeMs;
+ }
+
public ComponentName getComponent() {
return mTargetComponent;
}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 177e2f5..1fb8f87 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -126,7 +126,7 @@
private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
- final IIntentSender target = mService.mAm.getIntentSenderLocked(
+ final IIntentSender target = mService.getIntentSenderLocked(
INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
null /*resultCode*/, 0 /*requestCode*/,
new Intent[] { mIntent }, new String[] { mResolvedType },
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bcb2b9b..8236bd0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -783,7 +783,7 @@
if (aInfo != null) {
if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
aInfo.packageName, userId)) {
- IIntentSender target = mService.mAm.getIntentSenderLocked(
+ IIntentSender target = mService.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
callingUid, userId, null, null, 0, new Intent[]{intent},
new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -945,7 +945,8 @@
auxiliaryResponse == null ? null : auxiliaryResponse.filters);
}
- void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
+ void postStartActivityProcessing(ActivityRecord r, int result,
+ ActivityStack startedActivityStack) {
if (ActivityManager.isStartResultFatalError(result)) {
return;
}
@@ -957,14 +958,6 @@
// about this, so it waits for the new activity to become visible instead.
mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
- ActivityStack startedActivityStack = null;
- final ActivityStack currentStack = r.getStack();
- if (currentStack != null) {
- startedActivityStack = currentStack;
- } else if (mTargetStack != null) {
- startedActivityStack = targetStack;
- }
-
if (startedActivityStack == null) {
return;
}
@@ -1086,9 +1079,9 @@
// This may be a heavy-weight process! Check to see if we already
// have another, different heavy-weight process running.
if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
- final ProcessRecord heavy = mService.mAm.mHeavyWeightProcess;
- if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
- || !heavy.processName.equals(aInfo.processName))) {
+ final WindowProcessController heavy = mService.mHeavyWeightProcess;
+ if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid
+ || !heavy.mName.equals(aInfo.processName))) {
int appCallingUid = callingUid;
if (caller != null) {
ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller);
@@ -1103,7 +1096,7 @@
}
}
- IIntentSender target = mService.mAm.getIntentSenderLocked(
+ IIntentSender target = mService.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, "android",
appCallingUid, userId, null, null, 0, new Intent[] { intent },
new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
@@ -1116,8 +1109,7 @@
}
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
new IntentSender(target));
- heavy.getWindowProcessController().updateIntentForHeavyWeightActivity(
- newIntent);
+ heavy.updateIntentForHeavyWeightActivity(newIntent);
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
aInfo.packageName);
newIntent.setFlags(intent.getFlags());
@@ -1162,6 +1154,9 @@
mService.updateConfigurationLocked(globalConfig, null, false);
}
+ // Notify ActivityMetricsLogger that the activity has launched. ActivityMetricsLogger
+ // will then wait for the windows to be drawn and populate WaitResult.
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
if (outResult != null) {
outResult.result = res;
@@ -1186,7 +1181,6 @@
outResult.timeout = false;
outResult.who = r.realActivity;
outResult.totalTime = 0;
- outResult.thisTime = 0;
break;
}
case START_TASK_TO_FRONT: {
@@ -1196,10 +1190,9 @@
outResult.timeout = false;
outResult.who = r.realActivity;
outResult.totalTime = 0;
- outResult.thisTime = 0;
} else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mSupervisor.waitActivityVisible(r.realActivity, outResult);
+ final long startTimeMs = SystemClock.uptimeMillis();
+ mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs);
// Note: the timeout variable is not currently not ever set.
do {
try {
@@ -1213,7 +1206,6 @@
}
}
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
return res;
}
}
@@ -1240,23 +1232,41 @@
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
int result = START_CANCELED;
+ final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
} finally {
- // 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();
- if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
- stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
- null /* intentResultData */, "startActivity", true /* oomAdj */);
+ final ActivityStack currentStack = r.getStack();
+ startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+ if (ActivityManager.isStartResultSuccessful(result)) {
+ if (startedActivityStack != null) {
+ // If there is no state change (e.g. a resumed activity is reparented to
+ // top of another display) to trigger a visibility/configuration checking,
+ // we have to update the configuration for changing to different display.
+ final ActivityRecord currentTop =
+ startedActivityStack.topRunningActivityLocked();
+ if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+ mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+ }
+ } else {
+ // 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();
+ if (stack != null) {
+ stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+ null /* intentResultData */, "startActivity", true /* oomAdj */);
+ }
}
mService.mWindowManager.continueSurfaceLayout();
}
- postStartActivityProcessing(r, result, mTargetStack);
+ postStartActivityProcessing(r, result, startedActivityStack);
return result;
}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 212844a..36261b5 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -125,7 +125,11 @@
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
+import android.app.INotificationManager;
import android.app.ITaskStackListener;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ProfilerInfo;
import android.app.RemoteAction;
@@ -145,11 +149,13 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -208,10 +214,13 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.LocalServices;
@@ -230,7 +239,9 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -266,6 +277,8 @@
ActivityManagerInternal mAmInternal;
UriGrantsManagerInternal mUgmInternal;
private PackageManagerInternal mPmInternal;
+ private ActivityTaskManagerInternal mInternal;
+ PendingIntentController mPendingIntentController;
/* Global service lock used by the package the owns this service. */
Object mGlobalLock;
ActivityStackSupervisor mStackSupervisor;
@@ -278,6 +291,8 @@
final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
/** This is the process holding what we currently consider to be the "home" activity. */
WindowProcessController mHomeProcess;
+ /** The currently running heavy-weight process, if any. */
+ WindowProcessController mHeavyWeightProcess = null;
/**
* This is the process holding the activity the user last visited that is in a different process
* from the one they are currently in.
@@ -475,6 +490,12 @@
private AppWarnings mAppWarnings;
+ /**
+ * Packages that the user has asked to have run in screen size
+ * compatibility mode instead of filling the screen.
+ */
+ CompatModePackages mCompatModePackages;
+
private FontScaleSettingObserver mFontScaleSettingObserver;
private final class FontScaleSettingObserver extends ContentObserver {
@@ -607,8 +628,10 @@
mGlobalLock = mAm;
mH = new H(mAm.mHandlerThread.getLooper());
mUiHandler = new UiHandler();
- mAppWarnings = new AppWarnings(
- this, mUiContext, mH, mUiHandler, SystemServiceManager.ensureSystemDir());
+ final File systemDir = SystemServiceManager.ensureSystemDir();
+ mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
+ mCompatModePackages = new CompatModePackages(this, systemDir, mH);
+ mPendingIntentController = mAm.mPendingIntentController;
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
@@ -631,6 +654,11 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
}
+ int increaseConfigurationSeqLocked() {
+ mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+ return mConfigurationSeq;
+ }
+
protected ActivityStackSupervisor createStackSupervisor() {
final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
supervisor.initialize();
@@ -686,8 +714,49 @@
return mLockTaskController;
}
+ /**
+ * Return the global configuration used by the process corresponding to the input pid. This is
+ * usually the global configuration with some overrides specific to that process.
+ */
+ Configuration getGlobalConfigurationForCallingPid() {
+ final int pid = Binder.getCallingPid();
+ if (pid == MY_PID || pid < 0) {
+ return getGlobalConfiguration();
+ }
+ synchronized (mGlobalLock) {
+ final WindowProcessController app = mPidMap.get(pid);
+ return app != null ? app.getConfiguration() : getGlobalConfiguration();
+ }
+ }
+
+ /**
+ * Return the device configuration info used by the process corresponding to the input pid.
+ * The value is consistent with the global configuration for the process.
+ */
+ @Override
+ public ConfigurationInfo getDeviceConfigurationInfo() {
+ ConfigurationInfo config = new ConfigurationInfo();
+ synchronized (mGlobalLock) {
+ final Configuration globalConfig = getGlobalConfigurationForCallingPid();
+ config.reqTouchScreen = globalConfig.touchscreen;
+ config.reqKeyboardType = globalConfig.keyboard;
+ config.reqNavigation = globalConfig.navigation;
+ if (globalConfig.navigation == Configuration.NAVIGATION_DPAD
+ || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) {
+ config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+ }
+ if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED
+ && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
+ config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+ }
+ config.reqGlEsVersion = mAm.GL_ES_VERSION;
+ }
+ return config;
+ }
+
private void start() {
- LocalServices.addService(ActivityTaskManagerInternal.class, new LocalService());
+ mInternal = new LocalService();
+ LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
public static final class Lifecycle extends SystemService {
@@ -1461,16 +1530,13 @@
@Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
- ApplicationInfo ai;
synchronized (mGlobalLock) {
final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
- ai = r.info.applicationInfo;
+ return mCompatModePackages.computeCompatModeLocked(r.info.applicationInfo);
}
-
- return mAmInternal.getPackageScreenCompatMode(ai);
}
@Override
@@ -1485,9 +1551,8 @@
return;
}
ai = r.info.applicationInfo;
+ mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
}
-
- mAmInternal.setPackageScreenCompatMode(ai, mode);
}
@Override
@@ -1895,10 +1960,12 @@
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
Slog.d(TAG, "Could not find task for id: "+ taskId);
+ SafeActivityOptions.abort(options);
return;
}
if (getLockTaskController().isLockTaskModeViolation(task)) {
Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+ SafeActivityOptions.abort(options);
return;
}
ActivityOptions realOptions = options != null
@@ -1918,7 +1985,6 @@
} finally {
Binder.restoreCallingIdentity(origId);
}
- SafeActivityOptions.abort(options);
}
boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
@@ -4112,7 +4178,7 @@
return mVrController.shouldDisableNonVrUiLocked();
}
- void applyUpdateVrModeLocked(ActivityRecord r) {
+ private void applyUpdateVrModeLocked(ActivityRecord r) {
// VR apps are expected to run in a main display. If an app is turning on VR for
// itself, but lives in a dynamic stack, then make sure that it is moved to the main
// fullscreen stack before enabling VR Mode.
@@ -4143,6 +4209,40 @@
});
}
+ @Override
+ public int getPackageScreenCompatMode(String packageName) {
+ enforceNotIsolatedCaller("getPackageScreenCompatMode");
+ synchronized (mGlobalLock) {
+ return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
+ }
+ }
+
+ @Override
+ public void setPackageScreenCompatMode(String packageName, int mode) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+ "setPackageScreenCompatMode");
+ synchronized (mGlobalLock) {
+ mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
+ }
+ }
+
+ @Override
+ public boolean getPackageAskScreenCompat(String packageName) {
+ enforceNotIsolatedCaller("getPackageAskScreenCompat");
+ synchronized (mGlobalLock) {
+ return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
+ }
+ }
+
+ @Override
+ public void setPackageAskScreenCompat(String packageName, boolean ask) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+ "setPackageAskScreenCompat");
+ synchronized (mGlobalLock) {
+ mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
+ }
+ }
+
ActivityStack getTopDisplayFocusedStack() {
return mStackSupervisor.getTopDisplayFocusedStack();
}
@@ -4215,7 +4315,7 @@
public Configuration getConfiguration() {
Configuration ci;
synchronized(mGlobalLock) {
- ci = new Configuration(getGlobalConfiguration());
+ ci = new Configuration(getGlobalConfigurationForCallingPid());
ci.userSetLocale = false;
}
return ci;
@@ -4371,8 +4471,7 @@
locales.get(bestLocaleIndex)));
}
- mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
- mTempConfig.seq = mConfigurationSeq;
+ mTempConfig.seq = increaseConfigurationSeqLocked();
// Update stored global config and notify everyone about the change.
mStackSupervisor.onConfigurationChanged(mTempConfig);
@@ -4406,6 +4505,7 @@
mAm.mHandler.sendMessage(msg);
}
+ // TODO: Consider using mPidMap to update configurations for processes.
for (int i = mAm.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mAm.mLruProcesses.get(i);
try {
@@ -4688,11 +4788,7 @@
return mSleeping;
}
- /**
- * Update AMS states when an activity is resumed. This should only be called by
- * {@link ActivityStack#onActivityStateChanged(
- * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
- */
+ /** Update AMS states when an activity is resumed. */
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
if (task.isActivityTypeStandard()) {
@@ -4812,6 +4908,155 @@
mH.post(mAmInternal::updateOomAdj);
}
+ void updateCpuStats() {
+ mH.post(mAmInternal::updateCpuStats);
+ }
+
+ void updateUsageStats(ActivityRecord component, boolean resumed) {
+ final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
+ mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
+ mH.sendMessage(m);
+ }
+
+ void setBooting(boolean booting) {
+ mAmInternal.setBooting(booting);
+ }
+
+ boolean isBooting() {
+ return mAmInternal.isBooting();
+ }
+
+ void setBooted(boolean booted) {
+ mAmInternal.setBooted(booted);
+ }
+
+ boolean isBooted() {
+ return mAmInternal.isBooted();
+ }
+
+ void postFinishBooting(boolean finishBooting, boolean enableScreen) {
+ mH.post(() -> {
+ if (finishBooting) {
+ mAmInternal.finishBooting();
+ }
+ if (enableScreen) {
+ mInternal.enableScreenAfterBoot(isBooted());
+ }
+ });
+ }
+
+ void setHeavyWeightProcess(ActivityRecord root) {
+ mHeavyWeightProcess = root.app;
+ final Message m = PooledLambda.obtainMessage(
+ ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
+ root.app, root.intent, root.userId);
+ mH.sendMessage(m);
+ }
+
+ void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
+ if (mHeavyWeightProcess == null || mHeavyWeightProcess != proc) {
+ return;
+ }
+
+ mHeavyWeightProcess = null;
+ final Message m = PooledLambda.obtainMessage(
+ ActivityTaskManagerService::cancelHeavyWeightProcessNotification, this,
+ proc.mUserId);
+ mH.sendMessage(m);
+ }
+
+ private void cancelHeavyWeightProcessNotification(int userId) {
+ final INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+
+ }
+
+ private void postHeavyWeightProcessNotification(
+ WindowProcessController proc, Intent intent, int userId) {
+ if (proc == null) {
+ return;
+ }
+
+ final INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(proc.mInfo.packageName, 0);
+ String text = mContext.getString(R.string.heavy_weight_notification,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ Notification notification =
+ new Notification.Builder(context,
+ SystemNotificationChannels.HEAVY_WEIGHT_APP)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(text)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(text)
+ .setContentText(
+ mContext.getText(R.string.heavy_weight_notification_detail))
+ .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
+ intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(userId)))
+ .build();
+ try {
+ inm.enqueueNotificationWithTag("android", "android", null,
+ SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, notification, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error showing notification for heavy-weight app", e);
+ } catch (RemoteException e) {
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for heavy notification", e);
+ }
+
+ }
+
+ IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId,
+ IBinder token, String resultWho, int requestCode, Intent[] intents,
+ String[] resolvedTypes, int flags, Bundle bOptions) {
+
+ ActivityRecord activity = null;
+ if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+ activity = ActivityRecord.isInStackLocked(token);
+ if (activity == null) {
+ Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
+ return null;
+ }
+ if (activity.finishing) {
+ Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
+ return null;
+ }
+ }
+
+ final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
+ callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+ bOptions);
+ final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
+ if (noCreate) {
+ return rec;
+ }
+ if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+ if (activity.pendingResults == null) {
+ activity.pendingResults = new HashSet<>();
+ }
+ activity.pendingResults.add(rec.ref);
+ }
+ return rec;
+ }
+
// TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
private void startTimeTrackingFocusedActivityLocked() {
final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
@@ -4888,6 +5133,10 @@
mH.post(() -> mAmInternal.scheduleAppGcs());
}
+ CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
+ return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
+ }
+
/**
* Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The
* PackageManager could be unavailable at construction time and therefore needs to be accessed
@@ -5099,6 +5348,31 @@
}
@Override
+ public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+ String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+ boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+ synchronized (mGlobalLock) {
+ return getActivityStartController().startActivitiesInPackage(uid, callingPackage,
+ intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
+ originatingPendingIntent);
+ }
+ }
+
+ @Override
+ public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
+ String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+ int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+ PendingIntentRecord originatingPendingIntent) {
+ synchronized (mGlobalLock) {
+ return getActivityStartController().startActivityInPackage(uid, realCallingPid,
+ realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
+ requestCode, startFlags, options, userId, inTask, reason,
+ validateIncomingUser, originatingPendingIntent);
+ }
+ }
+
+ @Override
public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
Intent intent, Bundle options, int userId) {
return ActivityTaskManagerService.this.startActivityAsUser(
@@ -5274,6 +5548,28 @@
}
@Override
+ public boolean isHeavyWeightProcess(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ return proc == mHeavyWeightProcess;
+ }
+ }
+
+ @Override
+ public void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(proc);
+ }
+ }
+
+ @Override
+ public void finishHeavyWeightApp() {
+ synchronized (mGlobalLock) {
+ ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
+ mHeavyWeightProcess);
+ }
+ }
+
+ @Override
public boolean isSleeping() {
synchronized (mGlobalLock) {
return isSleepingLocked();
@@ -5382,6 +5678,7 @@
@Override
public void onPackageDataCleared(String name) {
synchronized (mGlobalLock) {
+ mCompatModePackages.handlePackageDataClearedLocked(name);
mAppWarnings.onPackageDataCleared(name);
}
}
@@ -5390,6 +5687,98 @@
public void onPackageUninstalled(String name) {
synchronized (mGlobalLock) {
mAppWarnings.onPackageUninstalled(name);
+ mCompatModePackages.handlePackageUninstalledLocked(name);
+ }
+ }
+
+ @Override
+ public void onPackageAdded(String name, boolean replacing) {
+ synchronized (mGlobalLock) {
+ mCompatModePackages.handlePackageAddedLocked(name, replacing);
+ }
+ }
+
+ @Override
+ public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
+ synchronized (mGlobalLock) {
+ return compatibilityInfoForPackageLocked(ai);
+ }
+ }
+
+ /**
+ * Set the corresponding display information for the process global configuration. To be
+ * called when we need to show IME on a different display.
+ *
+ * @param pid The process id associated with the IME window.
+ * @param displayId The ID of the display showing the IME.
+ */
+ @Override
+ public void onImeWindowSetOnDisplay(int pid, int displayId) {
+ if (pid == MY_PID || pid < 0) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG,
+ "Trying to update display configuration for system/invalid process.");
+ }
+ return;
+ }
+ mH.post(() -> {
+ synchronized (mGlobalLock) {
+ // Check if display is initialized in AM.
+ if (!mStackSupervisor.isDisplayAdded(displayId)) {
+ // Call 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=" + displayId);
+ }
+ return;
+ }
+ final WindowProcessController imeProcess = mPidMap.get(pid);
+ if (imeProcess == null) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Trying to update display configuration for invalid pid: "
+ + pid);
+ }
+ return;
+ }
+ // Fetch the current override configuration of the display and set it to the
+ // process global configuration.
+ imeProcess.onConfigurationChanged(
+ mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ }
+ });
+ }
+
+ @Override
+ public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
+ 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);
+ }
+ }
+ }
+
+ @Override
+ public void clearPendingResultForActivity(IBinder activityToken,
+ WeakReference<PendingIntentRecord> pir) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+ if (r != null && r.pendingResults != null) {
+ r.pendingResults.remove(pir);
+ }
+ }
+ }
+
+ @Override
+ public IIntentSender getIntentSender(int type, String packageName,
+ int callingUid, int userId, IBinder token, String resultWho,
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
+ Bundle bOptions) {
+ synchronized (mGlobalLock) {
+ return getIntentSenderLocked(type, packageName, callingUid, userId, token,
+ resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0a7e127..e2035f6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -302,7 +302,7 @@
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
- mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
+ mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.getReportedProcState());
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
@@ -741,7 +741,7 @@
// Show a permission review UI only for explicit broadcast from a foreground app
if (callerForeground && receiverRecord.intent.getComponent() != null) {
- IIntentSender target = mService.getIntentSenderLocked(
+ IIntentSender target = mService.mPendingIntentController.getIntentSender(
ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
receiverRecord.callingUid, receiverRecord.userId, null, null, 0,
new Intent[]{receiverRecord.intent},
@@ -1668,7 +1668,7 @@
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
|| mPendingBroadcast != null) {
boolean printed = false;
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 77efbfc..536f3a9 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -51,13 +51,13 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_AM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final AtomicFile mFile;
// Compatibility state: no longer ask user to select the mode.
- public static final int COMPAT_FLAG_DONT_ASK = 1<<0;
+ private static final int COMPAT_FLAG_DONT_ASK = 1<<0;
// Compatibility state: compatibility mode is enabled.
- public static final int COMPAT_FLAG_ENABLED = 1<<1;
+ private static final int COMPAT_FLAG_ENABLED = 1<<1;
private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
@@ -80,7 +80,7 @@
}
};
- public CompatModePackages(ActivityManagerService service, File systemDir, Handler handler) {
+ public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) {
mService = service;
mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode");
mHandler = new CompatHandler(handler.getLooper());
@@ -317,12 +317,12 @@
scheduleWrite();
- final ActivityStack stack = mService.mActivityTaskManager.getTopDisplayFocusedStack();
+ final ActivityStack stack = mService.getTopDisplayFocusedStack();
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
- for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord app = mService.mLruProcesses.get(i);
+ for (int i = mService.mAm.mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mService.mAm.mLruProcesses.get(i);
if (!app.pkgList.containsKey(packageName)) {
continue;
}
@@ -346,10 +346,10 @@
}
}
- void saveCompatModes() {
+ private void saveCompatModes() {
HashMap<String, Integer> pkgs;
- synchronized (mService) {
- pkgs = new HashMap<String, Integer>(mPackages);
+ synchronized (mService.mGlobalLock) {
+ pkgs = new HashMap<>(mPackages);
}
FileOutputStream fos = null;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index ed891df..0ef2a0a 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -87,9 +87,6 @@
# User switched
30041 am_switch_user (id|1|5)
-# Activity fully drawn time
-30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3)
-
# Activity set to resumed
30043 am_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3)
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index ee4e36f..cfe2829 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -29,16 +29,20 @@
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
+import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
+import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -58,19 +62,18 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
- private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private WindowManagerService mWindowManager;
private boolean mKeyguardShowing;
private boolean mAodShowing;
private boolean mKeyguardGoingAway;
- private boolean mOccluded;
private boolean mDismissalRequested;
- private ActivityRecord mDismissingKeyguardActivity;
private int mBeforeUnoccludeTransit;
private int mVisibilityTransactionDepth;
- private SleepToken mSleepToken;
+ // TODO(b/111955725): Support multiple external displays
private int mSecondaryDisplayShowing = INVALID_DISPLAY;
+ private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
+ private final ActivityTaskManagerService mService;
KeyguardController(ActivityTaskManagerService service,
ActivityStackSupervisor stackSupervisor) {
@@ -87,8 +90,8 @@
* on the given display, false otherwise
*/
boolean isKeyguardOrAodShowing(int displayId) {
- return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway &&
- (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
+ return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
+ && !isDisplayOccluded(displayId);
}
/**
@@ -96,8 +99,7 @@
* display, false otherwise
*/
boolean isKeyguardShowing(int displayId) {
- return mKeyguardShowing && !mKeyguardGoingAway &&
- (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
+ return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
}
/**
@@ -133,6 +135,7 @@
if (showingChanged) {
dismissDockedStackIfNeeded();
setKeyguardGoingAway(false);
+ // TODO(b/113840485): Check usage for non-default display
mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay(
isKeyguardOrAodShowing(DEFAULT_DISPLAY));
if (keyguardShowing) {
@@ -248,7 +251,8 @@
// already the dismissing activity, in which case we don't allow it to repeatedly dismiss
// Keyguard.
return dismissKeyguard && canDismissKeyguard() && !mAodShowing
- && (mDismissalRequested || r != mDismissingKeyguardActivity);
+ && (mDismissalRequested
+ || getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r);
}
/**
@@ -259,44 +263,16 @@
}
private void visibilitiesUpdated() {
- final boolean lastOccluded = mOccluded;
- final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
- mOccluded = false;
- mDismissingKeyguardActivity = null;
-
+ boolean requestDismissKeyguard = false;
for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
-
- // Only the top activity of the focused stack on the default display may control
- // occluded state.
- if (display.mDisplayId == DEFAULT_DISPLAY
- && mStackSupervisor.isTopDisplayFocusedStack(stack)) {
-
- // A dismissing activity occludes Keyguard in the insecure case for legacy
- // reasons.
- final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
- mOccluded =
- stack.topActivityOccludesKeyguard()
- || (topDismissing != null
- && stack.topRunningActivityLocked() == topDismissing
- && canShowWhileOccluded(
- true /* dismissKeyguard */,
- false /* showWhenLocked */));
- }
-
- if (mDismissingKeyguardActivity == null
- && stack.getTopDismissingKeyguardActivity() != null) {
- mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
- }
- }
+ final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+ state.visibilitiesUpdated(this, display);
+ requestDismissKeyguard |= state.mRequestDismissKeyguard;
}
- mOccluded |= mWindowManager.isShowingDream();
- if (mOccluded != lastOccluded) {
- handleOccludedChanged();
- }
- if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) {
+
+ // Dismissing Keyguard happens globally using the information from all displays.
+ if (requestDismissKeyguard) {
handleDismissKeyguard();
}
}
@@ -305,7 +281,7 @@
* Called when occluded state changed.
*/
private void handleOccludedChanged() {
- mWindowManager.onKeyguardOccludedChanged(mOccluded);
+ mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
if (isKeyguardLocked()) {
mWindowManager.deferSurfaceLayout();
try {
@@ -322,14 +298,13 @@
}
/**
- * Called when somebody might want to dismiss the Keyguard.
+ * Called when somebody wants to dismiss the Keyguard via the flag.
*/
private void handleDismissKeyguard() {
// 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 (!mOccluded && mDismissingKeyguardActivity != null
- && mWindowManager.isKeyguardSecure()) {
+ if (mWindowManager.isKeyguardSecure()) {
mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
mDismissalRequested = true;
@@ -345,6 +320,10 @@
}
}
+ private boolean isDisplayOccluded(int displayId) {
+ return getDisplay(displayId).mOccluded;
+ }
+
/**
* @return true if Keyguard can be currently dismissed without entering credentials.
*/
@@ -355,12 +334,14 @@
private int resolveOccludeTransit() {
if (mBeforeUnoccludeTransit != TRANSIT_UNSET
&& mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
- && mOccluded) {
+ // TODO(b/113840485): Handle app transition for individual display.
+ && isDisplayOccluded(DEFAULT_DISPLAY)) {
// Reuse old transit in case we are occluding Keyguard again, meaning that we never
// actually occclude/unocclude Keyguard, but just run a normal transition.
return mBeforeUnoccludeTransit;
- } else if (!mOccluded) {
+ // TODO(b/113840485): Handle app transition for individual display.
+ } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
// Save transit in case we dismiss/occlude Keyguard shortly after.
mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition();
@@ -371,7 +352,8 @@
}
private void dismissDockedStackIfNeeded() {
- if (mKeyguardShowing && mOccluded) {
+ // TODO(b/113840485): Handle docked stack for individual display.
+ if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
// The lock screen is currently showing, but is occluded by a window that can
// 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
@@ -386,11 +368,116 @@
}
private void updateKeyguardSleepToken() {
- if (mSleepToken == null && isKeyguardOrAodShowing(DEFAULT_DISPLAY)) {
- mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
- } else if (mSleepToken != null && !isKeyguardOrAodShowing(DEFAULT_DISPLAY)) {
- mSleepToken.release();
- mSleepToken = null;
+ for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+ if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
+ state.acquiredSleepToken();
+ } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) {
+ state.releaseSleepToken();
+ }
+ }
+ }
+
+ private KeyguardDisplayState getDisplay(int displayId) {
+ if (mDisplayStates.get(displayId) == null) {
+ mDisplayStates.append(displayId,
+ new KeyguardDisplayState(mService, displayId));
+ }
+ return mDisplayStates.get(displayId);
+ }
+
+ void onDisplayRemoved(int displayId) {
+ if (mDisplayStates.get(displayId) != null) {
+ mDisplayStates.get(displayId).onRemoved();
+ mDisplayStates.remove(displayId);
+ }
+ }
+
+ /** Represents Keyguard state per individual display. */
+ private static class KeyguardDisplayState {
+ private final int mDisplayId;
+ private boolean mOccluded;
+ private ActivityRecord mDismissingKeyguardActivity;
+ private boolean mRequestDismissKeyguard;
+ private final ActivityTaskManagerService mService;
+ private SleepToken mSleepToken;
+
+ KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
+ mService = service;
+ mDisplayId = displayId;
+ }
+
+ void onRemoved() {
+ mDismissingKeyguardActivity = null;
+ releaseSleepToken();
+ }
+
+ void acquiredSleepToken() {
+ if (mSleepToken == null) {
+ mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
+ }
+ }
+
+ void releaseSleepToken() {
+ if (mSleepToken != null) {
+ mSleepToken.release();
+ mSleepToken = null;
+ }
+ }
+
+ void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) {
+ final boolean lastOccluded = mOccluded;
+ final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
+ mRequestDismissKeyguard = false;
+ mOccluded = false;
+ mDismissingKeyguardActivity = null;
+
+ // Only the top activity of the focused stack on each display may control it's
+ // occluded state.
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack != null) {
+ final ActivityRecord topDismissing =
+ focusedStack.getTopDismissingKeyguardActivity();
+ mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
+ && focusedStack.topRunningActivityLocked() == topDismissing
+ && controller.canShowWhileOccluded(
+ true /* dismissKeyguard */,
+ false /* showWhenLocked */));
+ if (focusedStack.getTopDismissingKeyguardActivity() != null) {
+ mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+ }
+ mOccluded |= controller.mWindowManager.isShowingDream();
+ }
+
+ // TODO(b/113840485): Handle app transition for individual display.
+ // For now, only default display can change occluded.
+ if (lastOccluded != mOccluded && mDisplayId == DEFAULT_DISPLAY) {
+ controller.handleOccludedChanged();
+ }
+ if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
+ && mDismissingKeyguardActivity != null
+ && controller.mWindowManager.isKeyguardSecure()) {
+ mRequestDismissKeyguard = true;
+ }
+ }
+
+ void dumpStatus(PrintWriter pw, String prefix) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(prefix);
+ sb.append(" Occluded=").append(mOccluded)
+ .append(" DismissingKeyguardActivity=")
+ .append(mDismissingKeyguardActivity)
+ .append(" at display=")
+ .append(mDisplayId);
+ pw.println(sb.toString());
+ }
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(DISPLAY_ID, mDisplayId);
+ proto.write(KEYGUARD_OCCLUDED, mOccluded);
+ proto.end(token);
}
}
@@ -399,8 +486,7 @@
pw.println(prefix + " mKeyguardShowing=" + mKeyguardShowing);
pw.println(prefix + " mAodShowing=" + mAodShowing);
pw.println(prefix + " mKeyguardGoingAway=" + mKeyguardGoingAway);
- pw.println(prefix + " mOccluded=" + mOccluded);
- pw.println(prefix + " mDismissingKeyguardActivity=" + mDismissingKeyguardActivity);
+ dumpDisplayStates(pw, prefix);
pw.println(prefix + " mDismissalRequested=" + mDismissalRequested);
pw.println(prefix + " mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
}
@@ -408,7 +494,19 @@
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
- proto.write(KEYGUARD_OCCLUDED, mOccluded);
+ writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES);
proto.end(token);
}
+
+ private void dumpDisplayStates(PrintWriter pw, String prefix) {
+ for (int i = 0; i < mDisplayStates.size(); i++) {
+ mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
+ }
+ }
+
+ private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
+ for (int i = 0; i < mDisplayStates.size(); i++) {
+ mDisplayStates.valueAt(i).writeToProto(proto, fieldId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/LaunchTimeTracker.java b/services/core/java/com/android/server/am/LaunchTimeTracker.java
deleted file mode 100644
index ee86969..0000000
--- a/services/core/java/com/android/server/am/LaunchTimeTracker.java
+++ /dev/null
@@ -1,86 +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.am;
-
-import android.app.WaitResult;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.SparseArray;
-
-/**
- * Tracks launch time of apps to be reported by {@link WaitResult}. Note that this is slightly
- * different from {@link ActivityMetricsLogger}, but should eventually merged with it.
- */
-class LaunchTimeTracker {
-
- private final SparseArray<Entry> mWindowingModeLaunchTime = new SparseArray<>();
-
- void setLaunchTime(ActivityRecord r) {
- Entry entry = mWindowingModeLaunchTime.get(r.getWindowingMode());
- if (entry == null){
- entry = new Entry();
- mWindowingModeLaunchTime.append(r.getWindowingMode(), entry);
- }
- entry.setLaunchTime(r);
- }
-
- void stopFullyDrawnTraceIfNeeded(int windowingMode) {
- final Entry entry = mWindowingModeLaunchTime.get(windowingMode);
- if (entry == null) {
- return;
- }
- entry.stopFullyDrawnTraceIfNeeded();
- }
-
- Entry getEntry(int windowingMode) {
- return mWindowingModeLaunchTime.get(windowingMode);
- }
-
- static class Entry {
-
- long mLaunchStartTime;
- long mFullyDrawnStartTime;
-
- void setLaunchTime(ActivityRecord r) {
- if (r.displayStartTime == 0) {
- r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis();
- if (mLaunchStartTime == 0) {
- startLaunchTraces(r.packageName);
- mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime;
- }
- } else if (mLaunchStartTime == 0) {
- startLaunchTraces(r.packageName);
- mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis();
- }
- }
-
- private void startLaunchTraces(String packageName) {
- if (mFullyDrawnStartTime != 0) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
- }
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0);
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
- }
-
- private void stopFullyDrawnTraceIfNeeded() {
- if (mFullyDrawnStartTime != 0 && mLaunchStartTime == 0) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
- mFullyDrawnStartTime = 0;
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index f9dccea0..a8e1ccc 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.os.FileUtils;
+import android.os.SystemProperties;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -36,14 +37,26 @@
* Static utility methods related to {@link MemoryStat}.
*/
final class MemoryStatUtil {
+ static final int BYTES_IN_KILOBYTE = 1024;
+ static final int PAGE_SIZE = 4096;
+
private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
+ /** True if device has per-app memcg */
+ 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 */
+ private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -51,19 +64,19 @@
private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
+ private static final Pattern RSS_HIGH_WATERMARK_IN_BYTES =
+ Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+
private static final int PGFAULT_INDEX = 9;
private static final int PGMAJFAULT_INDEX = 11;
- private static final int RSS_IN_BYTES_INDEX = 23;
-
- /** True if device has memcg */
- private static volatile Boolean sDeviceHasMemCg;
+ private static final int RSS_IN_PAGES_INDEX = 23;
private MemoryStatUtil() {}
/**
* Reads memory stat for a process.
*
- * Reads from memcg if available on device, else fallback to procfs.
+ * Reads from per-app memcg if available on device, else fallback to procfs.
* Returns null if no stats can be read.
*/
@Nullable
@@ -78,8 +91,15 @@
*/
@Nullable
static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
- final String path = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
- return parseMemoryStatFromMemcg(readFileContents(path));
+ 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;
}
/**
@@ -89,8 +109,14 @@
*/
@Nullable
static MemoryStat readMemoryStatFromProcfs(int pid) {
- final String path = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
- return parseMemoryStatFromProcfs(readFileContents(path));
+ final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
+ MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
+ if (stat == null) {
+ return null;
+ }
+ final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
+ stat.rssHighWatermarkInBytes = parseVmHWMFromProcfs(readFileContents(statusPath));
+ return stat;
}
private static String readFileContents(String path) {
@@ -111,7 +137,7 @@
/**
* Parses relevant statistics out from the contents of a memory.stat file in memcg.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @VisibleForTesting
@Nullable
static MemoryStat parseMemoryStatFromMemcg(String memoryStatContents) {
if (memoryStatContents == null || memoryStatContents.isEmpty()) {
@@ -121,22 +147,35 @@
final MemoryStat memoryStat = new MemoryStat();
Matcher m;
m = PGFAULT.matcher(memoryStatContents);
- memoryStat.pgfault = m.find() ? Long.valueOf(m.group(1)) : 0;
+ memoryStat.pgfault = m.find() ? Long.parseLong(m.group(1)) : 0;
m = PGMAJFAULT.matcher(memoryStatContents);
- memoryStat.pgmajfault = m.find() ? Long.valueOf(m.group(1)) : 0;
+ memoryStat.pgmajfault = m.find() ? Long.parseLong(m.group(1)) : 0;
m = RSS_IN_BYTES.matcher(memoryStatContents);
- memoryStat.rssInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
+ memoryStat.rssInBytes = m.find() ? Long.parseLong(m.group(1)) : 0;
m = CACHE_IN_BYTES.matcher(memoryStatContents);
- memoryStat.cacheInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
+ memoryStat.cacheInBytes = m.find() ? Long.parseLong(m.group(1)) : 0;
m = SWAP_IN_BYTES.matcher(memoryStatContents);
- memoryStat.swapInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
+ memoryStat.swapInBytes = m.find() ? Long.parseLong(m.group(1)) : 0;
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 a /proc/pid/stat file in procfs.
+ * Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @VisibleForTesting
@Nullable
static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
if (procStatContents == null || procStatContents.isEmpty()) {
@@ -148,23 +187,37 @@
return null;
}
- final MemoryStat memoryStat = new MemoryStat();
- memoryStat.pgfault = Long.valueOf(splits[PGFAULT_INDEX]);
- memoryStat.pgmajfault = Long.valueOf(splits[PGMAJFAULT_INDEX]);
- memoryStat.rssInBytes = Long.valueOf(splits[RSS_IN_BYTES_INDEX]);
- return memoryStat;
+ try {
+ final MemoryStat memoryStat = new MemoryStat();
+ memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]);
+ memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]);
+ memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE;
+ return memoryStat;
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ return null;
+ }
}
/**
- * Checks if memcg is available on device.
- *
- * Touches the filesystem to do the check.
+ * Parses RSS high watermark out from the contents of the /proc/pid/status file in procfs. The
+ * returned value is in bytes.
+ */
+ @VisibleForTesting
+ static long parseVmHWMFromProcfs(String procStatusContents) {
+ if (procStatusContents == null || procStatusContents.isEmpty()) {
+ return 0;
+ }
+ Matcher m = RSS_HIGH_WATERMARK_IN_BYTES.matcher(procStatusContents);
+ // Convert value read from /proc/pid/status from kilobytes to bytes.
+ return m.find() ? Long.parseLong(m.group(1)) * BYTES_IN_KILOBYTE : 0;
+ }
+
+ /**
+ * Returns whether per-app memcg is available on device.
*/
static boolean hasMemcg() {
- if (sDeviceHasMemCg == null) {
- sDeviceHasMemCg = (new File(MEMCG_TEST_PATH)).exists();
- }
- return sDeviceHasMemCg;
+ return DEVICE_HAS_PER_APP_MEMCG;
}
static final class MemoryStat {
@@ -178,5 +231,7 @@
long cacheInBytes;
/** Number of bytes of swap usage */
long swapInBytes;
+ /** Number of bytes of peak anonymous and swap cache memory */
+ long rssHighWatermarkInBytes;
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjProfiler.java b/services/core/java/com/android/server/am/OomAdjProfiler.java
index 6230e0d..71f0db5 100644
--- a/services/core/java/com/android/server/am/OomAdjProfiler.java
+++ b/services/core/java/com/android/server/am/OomAdjProfiler.java
@@ -91,9 +91,9 @@
return;
}
mSystemServerCpuTimeUpdateScheduled = true;
- BackgroundThread.getHandler().post(PooledLambda.obtainRunnable(
+ BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
OomAdjProfiler::updateSystemServerCpuTime,
- this, mOnBattery, mScreenOff).recycleOnUse());
+ this, mOnBattery, mScreenOff));
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
new file mode 100644
index 0000000..a9c00a7
--- /dev/null
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.am;
+
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.Activity;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.PendingIntent;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for managing pending intents.
+ *
+ * <p>This class uses {@link #mLock} to synchronize access to internal state and doesn't make use of
+ * {@link ActivityManagerService} lock since there can be direct calls into this class from outside
+ * AM. This helps avoid deadlocks.
+ */
+public class PendingIntentController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentController" : TAG_AM;
+ private static final String TAG_MU = TAG + POSTFIX_MU;
+
+ /** Lock for internal state. */
+ final Object mLock = new Object();
+ final Handler mH;
+ ActivityManagerInternal mAmInternal;
+ final UserController mUserController;
+ final ActivityTaskManagerInternal mAtmInternal;
+
+ /** Set of IntentSenderRecord objects that are currently active. */
+ final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
+ = new HashMap<>();
+
+ PendingIntentController(Looper looper, UserController userController) {
+ mH = new Handler(looper);
+ mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mUserController = userController;
+ }
+
+ void onActivityManagerInternalAdded() {
+ synchronized (mLock) {
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ }
+ }
+
+ PendingIntentRecord getIntentSender(int type, String packageName, int callingUid, int userId,
+ IBinder token, String resultWho, int requestCode, Intent[] intents,
+ String[] resolvedTypes, int flags, Bundle bOptions) {
+ synchronized (mLock) {
+ if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
+
+ // We're going to be splicing together extras before sending, so we're
+ // okay poking into any contained extras.
+ if (intents != null) {
+ for (int i = 0; i < intents.length; i++) {
+ intents[i].setDefusable(true);
+ }
+ }
+ Bundle.setDefusable(bOptions, true);
+
+ final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
+ final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
+ final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0;
+ flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_UPDATE_CURRENT);
+
+ PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, token,
+ resultWho, requestCode, intents, resolvedTypes, flags,
+ SafeActivityOptions.fromBundle(bOptions), userId);
+ WeakReference<PendingIntentRecord> ref;
+ ref = mIntentSenderRecords.get(key);
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec != null) {
+ if (!cancelCurrent) {
+ if (updateCurrent) {
+ if (rec.key.requestIntent != null) {
+ rec.key.requestIntent.replaceExtras(intents != null ?
+ intents[intents.length - 1] : null);
+ }
+ if (intents != null) {
+ intents[intents.length - 1] = rec.key.requestIntent;
+ rec.key.allIntents = intents;
+ rec.key.allResolvedTypes = resolvedTypes;
+ } else {
+ rec.key.allIntents = null;
+ rec.key.allResolvedTypes = null;
+ }
+ }
+ return rec;
+ }
+ makeIntentSenderCanceled(rec);
+ mIntentSenderRecords.remove(key);
+ }
+ if (noCreate) {
+ return rec;
+ }
+ rec = new PendingIntentRecord(this, key, callingUid);
+ mIntentSenderRecords.put(key, rec.ref);
+ return rec;
+ }
+ }
+
+ boolean removePendingIntentsForPackage(String packageName, int userId, int appId,
+ boolean doIt) {
+
+ boolean didSomething = false;
+ synchronized (mLock) {
+
+ // Remove pending intents. For now we only do this when force stopping users, because
+ // we have some problems when doing this for packages -- app widgets are not currently
+ // cleaned up for such packages, so they can be left with bad pending intents.
+ if (mIntentSenderRecords.size() <= 0) {
+ return false;
+ }
+
+ Iterator<WeakReference<PendingIntentRecord>> it
+ = mIntentSenderRecords.values().iterator();
+ while (it.hasNext()) {
+ WeakReference<PendingIntentRecord> wpir = it.next();
+ if (wpir == null) {
+ it.remove();
+ continue;
+ }
+ PendingIntentRecord pir = wpir.get();
+ if (pir == null) {
+ it.remove();
+ continue;
+ }
+ if (packageName == null) {
+ // Stopping user, remove all objects for the user.
+ if (pir.key.userId != userId) {
+ // Not the same user, skip it.
+ continue;
+ }
+ } else {
+ if (UserHandle.getAppId(pir.uid) != appId) {
+ // Different app id, skip it.
+ continue;
+ }
+ if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
+ // Different user, skip it.
+ continue;
+ }
+ if (!pir.key.packageName.equals(packageName)) {
+ // Different package, skip it.
+ continue;
+ }
+ }
+ if (!doIt) {
+ return true;
+ }
+ didSomething = true;
+ it.remove();
+ makeIntentSenderCanceled(pir);
+ if (pir.key.activity != null) {
+ final Message m = PooledLambda.obtainMessage(
+ PendingIntentController::clearPendingResultForActivity, this,
+ pir.key.activity, pir.ref);
+ mH.sendMessage(m);
+ }
+ }
+ }
+
+ return didSomething;
+ }
+
+ public void cancelIntentSender(IIntentSender sender) {
+ if (!(sender instanceof PendingIntentRecord)) {
+ return;
+ }
+ synchronized (mLock) {
+ final PendingIntentRecord rec = (PendingIntentRecord) sender;
+ try {
+ final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
+ if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
+ String msg = "Permission Denial: cancelIntentSender() from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " is not allowed to cancel package " + rec.key.packageName;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ } catch (RemoteException e) {
+ throw new SecurityException(e);
+ }
+ cancelIntentSender(rec, true);
+ }
+ }
+
+ public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) {
+ synchronized (mLock) {
+ makeIntentSenderCanceled(rec);
+ mIntentSenderRecords.remove(rec.key);
+ if (cleanActivity && rec.key.activity != null) {
+ final Message m = PooledLambda.obtainMessage(
+ PendingIntentController::clearPendingResultForActivity, this,
+ rec.key.activity, rec.ref);
+ mH.sendMessage(m);
+ }
+ }
+ }
+
+ private void makeIntentSenderCanceled(PendingIntentRecord rec) {
+ rec.canceled = true;
+ final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
+ if (callbacks != null) {
+ final Message m = PooledLambda.obtainMessage(
+ PendingIntentController::handlePendingIntentCancelled, this, callbacks);
+ mH.sendMessage(m);
+ }
+ }
+
+ private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) {
+ int N = callbacks.beginBroadcast();
+ for (int i = 0; i < N; i++) {
+ try {
+ callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
+ } catch (RemoteException e) {
+ // Process is not longer running...whatever.
+ }
+ }
+ callbacks.finishBroadcast();
+ // We have to clean up the RemoteCallbackList here, because otherwise it will
+ // needlessly hold the enclosed callbacks until the remote process dies.
+ callbacks.kill();
+ }
+
+ private void clearPendingResultForActivity(IBinder activityToken,
+ WeakReference<PendingIntentRecord> pir) {
+ mAtmInternal.clearPendingResultForActivity(activityToken, pir);
+ }
+
+ void dumpPendingIntents(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ synchronized (mLock) {
+ boolean printed = false;
+
+ pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
+
+ if (mIntentSenderRecords.size() > 0) {
+ // Organize these by package name, so they are easier to read.
+ final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>();
+ final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>();
+ final Iterator<WeakReference<PendingIntentRecord>> it
+ = mIntentSenderRecords.values().iterator();
+ while (it.hasNext()) {
+ WeakReference<PendingIntentRecord> ref = it.next();
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec == null) {
+ weakRefs.add(ref);
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
+ continue;
+ }
+ ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName);
+ if (list == null) {
+ list = new ArrayList<>();
+ byPackage.put(rec.key.packageName, list);
+ }
+ list.add(rec);
+ }
+ for (int i = 0; i < byPackage.size(); i++) {
+ ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i);
+ printed = true;
+ pw.print(" * "); pw.print(byPackage.keyAt(i));
+ pw.print(": "); pw.print(intents.size()); pw.println(" items");
+ for (int j = 0; j < intents.size(); j++) {
+ pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
+ if (dumpAll) {
+ intents.get(j).dump(pw, " ");
+ }
+ }
+ }
+ if (weakRefs.size() > 0) {
+ printed = true;
+ pw.println(" * WEAK REFS:");
+ for (int i = 0; i < weakRefs.size(); i++) {
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
+ }
+ }
+ }
+
+ if (!printed) {
+ pw.println(" (nothing)");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index ee1166e..b9c6fa6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -38,15 +38,16 @@
import android.util.TimeUtils;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Objects;
-final class PendingIntentRecord extends IIntentSender.Stub {
+public final class PendingIntentRecord extends IIntentSender.Stub {
private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM;
- final ActivityManagerService owner;
+ final PendingIntentController controller;
final Key key;
final int uid;
final WeakReference<PendingIntentRecord> ref;
@@ -62,7 +63,7 @@
final static class Key {
final int type;
final String packageName;
- final ActivityRecord activity;
+ final IBinder activity;
final String who;
final int requestCode;
final Intent requestIntent;
@@ -76,7 +77,7 @@
private static final int ODD_PRIME_NUMBER = 37;
- Key(int _t, String _p, ActivityRecord _a, String _w,
+ Key(int _t, String _p, IBinder _a, String _w,
int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) {
type = _t;
packageName = _p;
@@ -114,6 +115,7 @@
// + Integer.toHexString(hashCode));
}
+ @Override
public boolean equals(Object otherObj) {
if (otherObj == null) {
return false;
@@ -188,11 +190,11 @@
}
}
- PendingIntentRecord(ActivityManagerService _owner, Key _k, int _u) {
- owner = _owner;
+ PendingIntentRecord(PendingIntentController _controller, Key _k, int _u) {
+ controller = _controller;
key = _k;
uid = _u;
- ref = new WeakReference<PendingIntentRecord>(this);
+ ref = new WeakReference<>(this);
}
void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
@@ -247,189 +249,196 @@
}
int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
- IIntentReceiver finishedReceiver,
- String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
- int flagsMask, int flagsValues, Bundle options) {
+ IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
+ String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
if (intent != null) intent.setDefusable(true);
if (options != null) options.setDefusable(true);
- synchronized (owner) {
- if (!canceled) {
- sent = true;
- if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) {
- owner.cancelIntentSenderLocked(this, true);
- }
+ Long duration = null;
+ Intent finalIntent = null;
+ Intent[] allIntents = null;
+ String[] allResolvedTypes = null;
+ SafeActivityOptions mergedOptions = null;
+ synchronized (controller.mLock) {
+ if (canceled) {
+ return ActivityManager.START_CANCELED;
+ }
- Intent finalIntent = key.requestIntent != null
- ? new Intent(key.requestIntent) : new Intent();
+ sent = true;
+ if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {
+ controller.cancelIntentSender(this, true);
+ }
- final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;
- if (!immutable) {
- if (intent != null) {
- int changes = finalIntent.fillIn(intent, key.flags);
- if ((changes & Intent.FILL_IN_DATA) == 0) {
- resolvedType = key.requestResolvedType;
- }
- } else {
+ finalIntent = key.requestIntent != null ? new Intent(key.requestIntent) : new Intent();
+
+ final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;
+ if (!immutable) {
+ if (intent != null) {
+ int changes = finalIntent.fillIn(intent, key.flags);
+ if ((changes & Intent.FILL_IN_DATA) == 0) {
resolvedType = key.requestResolvedType;
}
- flagsMask &= ~Intent.IMMUTABLE_FLAGS;
- flagsValues &= flagsMask;
- finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues);
} else {
resolvedType = key.requestResolvedType;
}
-
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
-
- // Extract options before clearing calling identity
- SafeActivityOptions mergedOptions = key.options;
- if (mergedOptions == null) {
- mergedOptions = SafeActivityOptions.fromBundle(options);
- } else {
- mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
- }
-
- final long origId = Binder.clearCallingIdentity();
-
- if (whitelistDuration != null) {
- Long duration = whitelistDuration.get(whitelistToken);
- if (duration != null) {
- int procState = owner.getUidState(callingUid);
- if (!ActivityManager.isProcStateBackground(procState)) {
- StringBuilder tag = new StringBuilder(64);
- tag.append("pendingintent:");
- UserHandle.formatUid(tag, callingUid);
- tag.append(":");
- if (finalIntent.getAction() != null) {
- tag.append(finalIntent.getAction());
- } else if (finalIntent.getComponent() != null) {
- finalIntent.getComponent().appendShortString(tag);
- } else if (finalIntent.getData() != null) {
- tag.append(finalIntent.getData().toSafeString());
- }
- owner.tempWhitelistForPendingIntentLocked(callingPid,
- callingUid, uid, duration, tag.toString());
- } else {
- Slog.w(TAG, "Not doing whitelist " + this + ": caller state="
- + procState);
- }
- }
- }
-
- boolean sendFinish = finishedReceiver != null;
- int userId = key.userId;
- if (userId == UserHandle.USER_CURRENT) {
- userId = owner.mUserController.getCurrentOrTargetUserId();
- }
- int res = START_SUCCESS;
- switch (key.type) {
- case ActivityManager.INTENT_SENDER_ACTIVITY:
- try {
- // Note when someone has a pending intent, even from different
- // users, then there's no need to ensure the calling user matches
- // the target user, so validateIncomingUser is always false below.
-
- if (key.allIntents != null && key.allIntents.length > 1) {
- Intent[] allIntents = new Intent[key.allIntents.length];
- String[] allResolvedTypes = new String[key.allIntents.length];
- System.arraycopy(key.allIntents, 0, allIntents, 0,
- key.allIntents.length);
- if (key.allResolvedTypes != null) {
- System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
- key.allResolvedTypes.length);
- }
- allIntents[allIntents.length-1] = finalIntent;
- allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
-
- res = owner.mActivityTaskManager.getActivityStartController().startActivitiesInPackage(
- uid, key.packageName, allIntents, allResolvedTypes,
- resultTo, mergedOptions, userId,
- false /* validateIncomingUser */,
- this /* originatingPendingIntent */);
- } else {
- res = owner.mActivityTaskManager.getActivityStartController().startActivityInPackage(uid,
- callingPid, callingUid, key.packageName, finalIntent,
- resolvedType, resultTo, resultWho, requestCode, 0,
- mergedOptions, userId, null, "PendingIntentRecord",
- false /* validateIncomingUser */,
- this /* originatingPendingIntent */);
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Unable to send startActivity intent", e);
- }
- break;
- case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
- final ActivityStack stack = key.activity.getStack();
- if (stack != null) {
- stack.sendActivityResultLocked(-1, key.activity, key.who,
- key.requestCode, code, finalIntent);
- }
- break;
- case ActivityManager.INTENT_SENDER_BROADCAST:
- try {
- // If a completion callback has been requested, require
- // that the broadcast be delivered synchronously
- int sent = owner.broadcastIntentInPackage(key.packageName, uid,
- finalIntent, resolvedType, finishedReceiver, code, null, null,
- requiredPermission, options, (finishedReceiver != null),
- false, userId);
- if (sent == ActivityManager.BROADCAST_SUCCESS) {
- sendFinish = false;
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Unable to send startActivity intent", e);
- }
- break;
- case ActivityManager.INTENT_SENDER_SERVICE:
- case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
- try {
- owner.startServiceInPackage(uid, finalIntent, resolvedType,
- key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
- key.packageName, userId);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Unable to send startService intent", e);
- } catch (TransactionTooLargeException e) {
- res = ActivityManager.START_CANCELED;
- }
- break;
- }
-
- if (sendFinish && res != ActivityManager.START_CANCELED) {
- try {
- finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false, false, key.userId);
- } catch (RemoteException e) {
- }
- }
-
- Binder.restoreCallingIdentity(origId);
-
- return res;
+ flagsMask &= ~Intent.IMMUTABLE_FLAGS;
+ flagsValues &= flagsMask;
+ finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues);
+ } else {
+ resolvedType = key.requestResolvedType;
}
+
+ // Extract options before clearing calling identity
+ mergedOptions = key.options;
+ if (mergedOptions == null) {
+ mergedOptions = SafeActivityOptions.fromBundle(options);
+ } else {
+ mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+ }
+
+ if (whitelistDuration != null) {
+ duration = whitelistDuration.get(whitelistToken);
+ }
+
+ if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY
+ && key.allIntents != null && key.allIntents.length > 1) {
+ // Copy all intents and resolved types while we have the controller lock so we can
+ // use it later when the lock isn't held.
+ allIntents = new Intent[key.allIntents.length];
+ allResolvedTypes = new String[key.allIntents.length];
+ System.arraycopy(key.allIntents, 0, allIntents, 0, key.allIntents.length);
+ if (key.allResolvedTypes != null) {
+ System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
+ key.allResolvedTypes.length);
+ }
+ allIntents[allIntents.length - 1] = finalIntent;
+ allResolvedTypes[allResolvedTypes.length - 1] = resolvedType;
+ }
+
}
- return ActivityManager.START_CANCELED;
+ // We don't hold the controller lock beyond this point as we will be calling into AM and WM.
+
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final long origId = Binder.clearCallingIdentity();
+
+ int res = START_SUCCESS;
+ try {
+ if (duration != null) {
+ int procState = controller.mAmInternal.getUidProcessState(callingUid);
+ if (!ActivityManager.isProcStateBackground(procState)) {
+ StringBuilder tag = new StringBuilder(64);
+ tag.append("pendingintent:");
+ UserHandle.formatUid(tag, callingUid);
+ tag.append(":");
+ if (finalIntent.getAction() != null) {
+ tag.append(finalIntent.getAction());
+ } else if (finalIntent.getComponent() != null) {
+ finalIntent.getComponent().appendShortString(tag);
+ } else if (finalIntent.getData() != null) {
+ tag.append(finalIntent.getData().toSafeString());
+ }
+ controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid,
+ uid, duration, tag.toString());
+ } else {
+ Slog.w(TAG, "Not doing whitelist " + this + ": caller state=" + procState);
+ }
+ }
+
+ boolean sendFinish = finishedReceiver != null;
+ int userId = key.userId;
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = controller.mUserController.getCurrentOrTargetUserId();
+ }
+
+ switch (key.type) {
+ case ActivityManager.INTENT_SENDER_ACTIVITY:
+ try {
+ // Note when someone has a pending intent, even from different
+ // users, then there's no need to ensure the calling user matches
+ // the target user, so validateIncomingUser is always false below.
+
+ if (key.allIntents != null && key.allIntents.length > 1) {
+ res = controller.mAtmInternal.startActivitiesInPackage(
+ uid, key.packageName, allIntents, allResolvedTypes, resultTo,
+ mergedOptions, userId, false /* validateIncomingUser */,
+ this /* originatingPendingIntent */);
+ } else {
+ res = controller.mAtmInternal.startActivityInPackage(
+ uid, callingPid, callingUid, key.packageName, finalIntent,
+ resolvedType, resultTo, resultWho, requestCode, 0,
+ mergedOptions, userId, null, "PendingIntentRecord",
+ false /* validateIncomingUser */,
+ this /* originatingPendingIntent */);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Unable to send startActivity intent", e);
+ }
+ break;
+ case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
+ controller.mAtmInternal.sendActivityResult(-1, key.activity, key.who,
+ key.requestCode, code, finalIntent);
+ break;
+ case ActivityManager.INTENT_SENDER_BROADCAST:
+ try {
+ // If a completion callback has been requested, require
+ // that the broadcast be delivered synchronously
+ int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
+ uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
+ requiredPermission, options, (finishedReceiver != null),
+ false, userId);
+ if (sent == ActivityManager.BROADCAST_SUCCESS) {
+ sendFinish = false;
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Unable to send startActivity intent", e);
+ }
+ break;
+ case ActivityManager.INTENT_SENDER_SERVICE:
+ case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
+ try {
+ controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
+ key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
+ key.packageName, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Unable to send startService intent", e);
+ } catch (TransactionTooLargeException e) {
+ res = ActivityManager.START_CANCELED;
+ }
+ break;
+ }
+
+ if (sendFinish && res != ActivityManager.START_CANCELED) {
+ try {
+ finishedReceiver.performReceive(new Intent(finalIntent), 0,
+ null, null, false, false, key.userId);
+ } catch (RemoteException e) {
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return res;
}
@Override
protected void finalize() throws Throwable {
try {
if (!canceled) {
- owner.mHandler.sendMessage(owner.mHandler.obtainMessage(
- ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this));
+ controller.mH.sendMessage(PooledLambda.obtainMessage(
+ PendingIntentRecord::completeFinalize, this));
}
} finally {
super.finalize();
}
}
- public void completeFinalize() {
- synchronized(owner) {
- WeakReference<PendingIntentRecord> current =
- owner.mIntentSenderRecords.get(key);
+ private void completeFinalize() {
+ synchronized(controller.mLock) {
+ WeakReference<PendingIntentRecord> current = controller.mIntentSenderRecords.get(key);
if (current == ref) {
- owner.mIntentSenderRecords.remove(key);
+ controller.mIntentSenderRecords.remove(key);
}
}
}
diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index c5edb26..080fa2a 100644
--- a/services/core/java/com/android/server/am/PersistentConnection.java
+++ b/services/core/java/com/android/server/am/PersistentConnection.java
@@ -24,7 +24,7 @@
import android.os.IBinder;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.Slog;
+import android.util.Log;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -100,6 +100,15 @@
@GuardedBy("mLock")
private T mService;
+ @GuardedBy("mLock")
+ private int mNumConnected;
+
+ @GuardedBy("mLock")
+ private int mNumDisconnected;
+
+ @GuardedBy("mLock")
+ private int mNumBindingDied;
+
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -108,13 +117,15 @@
// Callback came in after PersistentConnection.unbind() was called.
// We just ignore this.
// (We've already called unbindService() already in unbind)
- Slog.w(mTag, "Connected: " + mComponentName.flattenToShortString()
+ Log.w(mTag, "Connected: " + mComponentName.flattenToShortString()
+ " u" + mUserId + " but not bound, ignore.");
return;
}
- Slog.i(mTag, "Connected: " + mComponentName.flattenToShortString()
+ Log.i(mTag, "Connected: " + mComponentName.flattenToShortString()
+ " u" + mUserId);
+ mNumConnected++;
+
mIsConnected = true;
mService = asInterface(service);
}
@@ -123,9 +134,11 @@
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
- Slog.i(mTag, "Disconnected: " + mComponentName.flattenToShortString()
+ Log.i(mTag, "Disconnected: " + mComponentName.flattenToShortString()
+ " u" + mUserId);
+ mNumDisconnected++;
+
cleanUpConnectionLocked();
}
}
@@ -136,13 +149,16 @@
synchronized (mLock) {
if (!mBound) {
// Callback came in late?
- Slog.w(mTag, "Binding died: " + mComponentName.flattenToShortString()
+ Log.w(mTag, "Binding died: " + mComponentName.flattenToShortString()
+ " u" + mUserId + " but not bound, ignore.");
return;
}
- Slog.w(mTag, "Binding died: " + mComponentName.flattenToShortString()
+ Log.w(mTag, "Binding died: " + mComponentName.flattenToShortString()
+ " u" + mUserId);
+
+ mNumBindingDied++;
+
scheduleRebindLocked();
}
}
@@ -170,6 +186,12 @@
return mComponentName;
}
+ public final int getUserId() {
+ return mUserId;
+ }
+
+ protected abstract int getBindFlags();
+
/**
* @return whether {@link #bind()} has been called and {@link #unbind()} hasn't.
*
@@ -237,15 +259,15 @@
final Intent service = new Intent().setComponent(mComponentName);
if (DEBUG) {
- Slog.d(mTag, "Attempting to connect to " + mComponentName);
+ Log.d(mTag, "Attempting to connect to " + mComponentName);
}
final boolean success = mContext.bindServiceAsUser(service, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ Context.BIND_AUTO_CREATE | getBindFlags(),
mHandler, UserHandle.of(mUserId));
if (!success) {
- Slog.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId
+ Log.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId
+ " failed.");
}
}
@@ -285,7 +307,7 @@
if (!mBound) {
return;
}
- Slog.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId);
+ Log.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId);
mBound = false;
mContext.unbindService(mServiceConnection);
@@ -303,7 +325,7 @@
unbindLocked();
if (!mRebindScheduled) {
- Slog.i(mTag, "Scheduling to reconnect in " + mNextBackoffMs + " ms (uptime)");
+ Log.i(mTag, "Scheduling to reconnect in " + mNextBackoffMs + " ms (uptime)");
mReconnectTime = injectUptimeMillis() + mNextBackoffMs;
@@ -323,10 +345,12 @@
synchronized (mLock) {
pw.print(prefix);
pw.print(mComponentName.flattenToShortString());
- pw.print(mBound ? " [bound]" : " [not bound]");
- pw.print(mIsConnected ? " [connected]" : " [not connected]");
+ pw.print(" u");
+ pw.print(mUserId);
+ pw.print(mBound ? " [bound]" : " [not bound]");
+ pw.print(mIsConnected ? " [connected]" : " [not connected]");
if (mRebindScheduled) {
- pw.print(" reconnect in ");
+ pw.print(" reconnect in ");
TimeUtils.formatDuration((mReconnectTime - injectUptimeMillis()), pw);
}
pw.println();
@@ -334,6 +358,16 @@
pw.print(prefix);
pw.print(" Next backoff(sec): ");
pw.print(mNextBackoffMs / 1000);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Connected: ");
+ pw.print(mNumConnected);
+ pw.print(" Disconnected: ");
+ pw.print(mNumDisconnected);
+ pw.print(" Died: ");
+ pw.print(mNumBindingDied);
+ pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index d3dc0f3..667d3fa 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -17,18 +17,10 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import android.os.Debug;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.util.EventLog;
-import android.util.Slog;
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.app.procstats.ProcessState;
-import com.android.internal.os.BatteryStatsImpl;
-
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.IApplicationThread;
@@ -36,7 +28,9 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.os.Binder;
+import android.os.Debug;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -44,10 +38,18 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.EventLog;
+import android.util.Slog;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.app.procstats.ProcessState;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BatteryStatsImpl;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -307,6 +309,7 @@
pw.print(prefix); pw.print("manageSpaceActivityName=");
pw.println(info.manageSpaceActivityName);
}
+
pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir);
pw.print(" publicDir="); pw.print(info.publicSourceDir);
pw.print(" data="); pw.println(info.dataDir);
@@ -520,7 +523,7 @@
}
ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
- int _uid) {
+ int _uid, Configuration config) {
mService = _service;
info = _info;
isolated = _info.uid != _uid;
@@ -534,7 +537,7 @@
removed = false;
lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
mWindowProcessController = new WindowProcessController(
- mService.mActivityTaskManager, info, processName, uid, userId, this, this);
+ mService.mActivityTaskManager, info, processName, uid, userId, this, this, config);
pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 8ce650c..6ffd8a9 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -23,8 +23,10 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.service.procstats.ProcessStatsServiceDumpProto;
+import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -47,6 +49,7 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
@@ -482,6 +485,23 @@
return finalRes;
}
+ static int parseSectionOptions(String optionsStr) {
+ final String sep = ",";
+ String[] sectionsStr = optionsStr.split(sep);
+ if (sectionsStr.length == 0) {
+ return ProcessStats.REPORT_ALL;
+ }
+ int res = 0;
+ List<String> optionStrList = Arrays.asList(ProcessStats.OPTIONS_STR);
+ for (String sectionStr : sectionsStr) {
+ int optionIndex = optionStrList.indexOf(sectionStr);
+ if (optionIndex != -1) {
+ res |= ProcessStats.OPTIONS[optionIndex];
+ }
+ }
+ return res;
+ }
+
public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
mAm.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS, null);
@@ -514,6 +534,95 @@
return current.marshall();
}
+ /**
+ * Get stats committed after highWaterMarkMs
+ * @param highWaterMarkMs Report stats committed after this time.
+ * @param section Integer mask to indicage which sections to include in the stats.
+ * @param doAggregate Whether to aggregate the stats or keep them separated.
+ * @return List of proto binary of individual commit files or one that is merged from them.
+ */
+ @Override
+ public long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate,
+ List<ParcelFileDescriptor> committedStats) {
+ mAm.mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS, null);
+
+ ProcessStats mergedStats = new ProcessStats(false);
+ long newHighWaterMark = highWaterMarkMs;
+ mWriteLock.lock();
+ try {
+ ArrayList<String> files = getCommittedFiles(0, false, true);
+ if (files != null) {
+ String highWaterMarkStr =
+ DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString();
+ ProcessStats stats = new ProcessStats(false);
+ for (int i = files.size() - 1; i >= 0; i--) {
+ String fileName = files.get(i);
+ try {
+ String startTimeStr = fileName.substring(
+ fileName.lastIndexOf(STATE_FILE_PREFIX)
+ + STATE_FILE_PREFIX.length(),
+ fileName.lastIndexOf(STATE_FILE_SUFFIX));
+ if (startTimeStr.compareToIgnoreCase(highWaterMarkStr) > 0) {
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+ new File(fileName),
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ stats.reset();
+ stats.read(is);
+ is.close();
+ if (stats.mTimePeriodStartClock > newHighWaterMark) {
+ newHighWaterMark = stats.mTimePeriodStartClock;
+ }
+ if (doAggregate) {
+ mergedStats.add(stats);
+ } else {
+ committedStats.add(protoToParcelFileDescriptor(stats, section));
+ }
+ if (stats.mReadError != null) {
+ Log.w(TAG, "Failure reading process stats: " + stats.mReadError);
+ continue;
+ }
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure opening procstat file " + fileName, e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "Failure to read and parse commit file " + fileName, e);
+ }
+ }
+ if (doAggregate) {
+ committedStats.add(protoToParcelFileDescriptor(mergedStats, section));
+ }
+ return newHighWaterMark;
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure opening procstat file", e);
+ } finally {
+ mWriteLock.unlock();
+ }
+ return newHighWaterMark;
+ }
+
+ private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
+ throws IOException {
+ final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+ Thread thr = new Thread("ProcessStats pipe output") {
+ public void run() {
+ try {
+ FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
+ final ProtoOutputStream proto = new ProtoOutputStream(fout);
+ stats.writeToProto(proto, stats.mTimePeriodEndRealtime, section);
+ proto.flush();
+ fout.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure writing pipe", e);
+ }
+ }
+ };
+ thr.start();
+ return fds[0];
+ }
+
public ParcelFileDescriptor getStatsOverTime(long minTime) {
mAm.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS, null);
@@ -594,7 +703,7 @@
private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now,
String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails,
- boolean dumpAll, boolean activeOnly) {
+ boolean dumpAll, boolean activeOnly, int section) {
ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
- (ProcessStats.COMMIT_PERIOD/2));
if (pfd == null) {
@@ -609,11 +718,11 @@
return;
}
if (isCompact) {
- stats.dumpCheckinLocked(pw, reqPackage);
+ stats.dumpCheckinLocked(pw, reqPackage, section);
} else {
if (dumpDetails || dumpFullDetails) {
stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll,
- activeOnly);
+ activeOnly, section);
} else {
stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
@@ -643,6 +752,8 @@
pw.println(" --max: for -a, max num of historical batches to print.");
pw.println(" --active: only show currently active processes/services.");
pw.println(" --commit: commit current stats to disk and reset to start new stats.");
+ pw.println(" --section: proc|pkg-proc|pkg-svc|pkg-asc|pkg-all|all ");
+ pw.println(" options can be combined to select desired stats");
pw.println(" --reset: reset current stats, without committing.");
pw.println(" --clear: clear all stats; does both --reset and deletes old stats.");
pw.println(" --write: write current in-memory stats to disk.");
@@ -696,6 +807,7 @@
int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
boolean csvSepProcStats = true;
int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
+ int section = ProcessStats.REPORT_ALL;
if (args != null) {
for (int i=0; i<args.length; i++) {
String arg = args[i];
@@ -814,13 +926,14 @@
pw.println("Process stats committed.");
quit = true;
}
- } else if ("--reset".equals(arg)) {
- synchronized (mAm) {
- mProcessStats.resetSafely();
- mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
- pw.println("Process stats reset.");
- quit = true;
+ } else if ("--section".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --section");
+ dumpHelp(pw);
+ return;
}
+ section = parseSectionOptions(args[i]);
} else if ("--clear".equals(arg)) {
synchronized (mAm) {
mProcessStats.resetSafely();
@@ -946,7 +1059,7 @@
} else if (aggregateHours != 0) {
pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:");
dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
- dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+ dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
return;
} else if (lastIndex > 0) {
pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
@@ -968,7 +1081,7 @@
boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
if (isCheckin || isCompact) {
// Don't really need to lock because we uniquely own this object.
- processStats.dumpCheckinLocked(pw, reqPackage);
+ processStats.dumpCheckinLocked(pw, reqPackage, section);
} else {
pw.print("COMMITTED STATS FROM ");
pw.print(processStats.mTimePeriodStartClockStr);
@@ -976,7 +1089,7 @@
pw.println(":");
if (dumpDetails || dumpFullDetails) {
processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
- dumpAll, activeOnly);
+ dumpAll, activeOnly, section);
if (dumpAll) {
pw.print(" mFile="); pw.println(mFile.getBaseFile());
}
@@ -1015,7 +1128,7 @@
boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
if (isCheckin || isCompact) {
// Don't really need to lock because we uniquely own this object.
- processStats.dumpCheckinLocked(pw, reqPackage);
+ processStats.dumpCheckinLocked(pw, reqPackage, section);
} else {
if (sepNeeded) {
pw.println();
@@ -1031,7 +1144,7 @@
// much crud.
if (dumpFullDetails) {
processStats.dumpLocked(pw, reqPackage, now, false, false,
- false, activeOnly);
+ false, activeOnly, section);
} else {
processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
@@ -1054,7 +1167,7 @@
if (!isCheckin) {
synchronized (mAm) {
if (isCompact) {
- mProcessStats.dumpCheckinLocked(pw, reqPackage);
+ mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
} else {
if (sepNeeded) {
pw.println();
@@ -1062,7 +1175,7 @@
pw.println("CURRENT STATS:");
if (dumpDetails || dumpFullDetails) {
mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
- dumpAll, activeOnly);
+ dumpAll, activeOnly, section);
if (dumpAll) {
pw.print(" mFile="); pw.println(mFile.getBaseFile());
}
@@ -1078,11 +1191,11 @@
}
pw.println("AGGREGATED OVER LAST 24 HOURS:");
dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
- dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+ dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
pw.println();
pw.println("AGGREGATED OVER LAST 3 HOURS:");
dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
- dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+ dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
}
}
}
@@ -1099,7 +1212,9 @@
if (stats.mReadError != null) {
return;
}
- stats.writeToProto(proto, fieldId, now);
+ final long token = proto.start(fieldId);
+ stats.writeToProto(proto, now, ProcessStats.REPORT_ALL);
+ proto.end(token);
}
private void dumpProto(FileDescriptor fd) {
@@ -1109,7 +1224,9 @@
long now;
synchronized (mAm) {
now = SystemClock.uptimeMillis();
- mProcessStats.writeToProto(proto,ProcessStatsServiceDumpProto.PROCSTATS_NOW, now);
+ final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
+ mProcessStats.writeToProto(proto, now, ProcessStats.REPORT_ALL);
+ proto.end(token);
}
// aggregated over last 3 hours procstats
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index f7de7f4..fa0cb47 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -44,7 +44,7 @@
* the inner options. Also supports having two set of options: Once from the original caller, and
* once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
*/
-class SafeActivityOptions {
+public class SafeActivityOptions {
private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7256e23..ef8cb1c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -129,7 +129,8 @@
import java.util.ArrayList;
import java.util.Objects;
-class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
+// TODO: Make package private again once move to WM package is complete.
+public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -1521,14 +1522,14 @@
/**
* Check whether this task can be launched on the specified display.
+ *
* @param displayId Target display id.
- * @return {@code true} if either it is the default display or this activity is resizeable and
- * can be put a secondary screen.
+ * @return {@code true} if either it is the default display or this activity can be put on a
+ * secondary display.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- isResizeable(false /* checkSupportsPip */), -1 /* don't check PID */,
- -1 /* don't check UID */, null /* activityInfo */);
+ -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
}
/**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index bd412fc..8154062 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -850,10 +850,16 @@
}
void scheduleStartProfiles() {
- if (!mHandler.hasMessages(START_PROFILES_MSG)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
- DateUtils.SECOND_IN_MILLIS);
- }
+ // Parent user transition to RUNNING_UNLOCKING happens on FgThread, so it is busy, there is
+ // a chance the profile will reach RUNNING_LOCKED while parent is still locked, so no
+ // attempt will be made to unlock the profile. If we go via FgThread, this will be executed
+ // after the parent had chance to unlock fully.
+ FgThread.getHandler().post(() -> {
+ if (!mHandler.hasMessages(START_PROFILES_MSG)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
+ DateUtils.SECOND_IN_MILLIS);
+ }
+ });
}
void startProfiles() {
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index e5551b5..f6f4db6 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -17,7 +17,10 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -29,11 +32,13 @@
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import android.app.Activity;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.IApplicationThread;
+import android.app.servertransaction.ConfigurationChangeItem;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.os.Message;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -41,7 +46,7 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.util.function.pooled.PooledRunnable;
+import com.android.server.wm.ConfigurationContainer;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -57,9 +62,10 @@
* window manager so the window manager lock is held and appropriate permissions are checked before
* calls are allowed to proceed.
*/
-public class WindowProcessController {
+public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+ private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
// all about the first app in the process
final ApplicationInfo mInfo;
@@ -108,8 +114,12 @@
// any tasks this process had run root activities in
private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
+ // Last configuration that was reported to the process.
+ private final Configuration mLastReportedConfiguration;
+
WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
- int uid, int userId, Object owner, WindowProcessListener listener) {
+ int uid, int userId, Object owner, WindowProcessListener listener,
+ Configuration config) {
mInfo = info;
mName = name;
mUid = uid;
@@ -117,6 +127,10 @@
mOwner = owner;
mListener = listener;
mAtm = atm;
+ mLastReportedConfiguration = new Configuration();
+ if (config != null) {
+ onConfigurationChanged(config);
+ }
}
public void setPid(int pid) {
@@ -219,6 +233,21 @@
return mInstrumenting;
}
+ @Override
+ protected int getChildCount() {
+ return 0;
+ }
+
+ @Override
+ protected ConfigurationContainer getChildAt(int index) {
+ return null;
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return null;
+ }
+
public void addPackage(String packageName) {
synchronized (mAtm.mGlobalLock) {
mPkgList.add(packageName);
@@ -482,48 +511,94 @@
void clearProfilerIfNeeded() {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
- mAtm.mH.post(() -> mListener.clearProfilerIfNeeded());
+ mAtm.mH.sendMessage(PooledLambda.obtainMessage(
+ WindowProcessListener::clearProfilerIfNeeded, mListener));
}
void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
boolean activityChange, boolean updateOomAdj) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
- final Runnable r = PooledLambda.obtainRunnable(WindowProcessListener::updateProcessInfo,
+ final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
mListener, updateServiceConnectionActivities, updateLru, activityChange,
updateOomAdj);
- mAtm.mH.post(r);
+ mAtm.mH.sendMessage(m);
}
void updateServiceConnectionActivities() {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
- mAtm.mH.post(() -> mListener.updateServiceConnectionActivities());
+ mAtm.mH.sendMessage(PooledLambda.obtainMessage(
+ WindowProcessListener::updateServiceConnectionActivities, mListener));
}
void setPendingUiClean(boolean pendingUiClean) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
- final Runnable r = PooledLambda.obtainRunnable(
+ final Message m = PooledLambda.obtainMessage(
WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
- mAtm.mH.post(r);
+ mAtm.mH.sendMessage(m);
}
void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
- final Runnable r = PooledLambda.obtainRunnable(
+ final Message m = PooledLambda.obtainMessage(
WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
mListener, newState);
- mAtm.mH.post(r);
+ mAtm.mH.sendMessage(m);
}
void setRemoved(boolean removed) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
- final Runnable r = PooledLambda.obtainRunnable(
+ final Message m = PooledLambda.obtainMessage(
WindowProcessListener::setRemoved, mListener, removed);
- mAtm.mH.post(r);
+ mAtm.mH.sendMessage(m);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newGlobalConfig) {
+ super.onConfigurationChanged(newGlobalConfig);
+ updateConfiguration();
+ }
+
+ @Override
+ public void onOverrideConfigurationChanged(Configuration newOverrideConfig) {
+ super.onOverrideConfigurationChanged(newOverrideConfig);
+ updateConfiguration();
+ }
+
+ private void updateConfiguration() {
+ final Configuration config = getConfiguration();
+ if (mLastReportedConfiguration.diff(config) == 0) {
+ // Nothing changed.
+ return;
+ }
+
+ try {
+ if (mThread == null) {
+ return;
+ }
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName
+ + " new config " + config);
+ }
+ config.seq = mAtm.increaseConfigurationSeqLocked();
+ mAtm.getLifecycleManager().scheduleTransaction(mThread,
+ ConfigurationChangeItem.obtain(config));
+ setLastReportedConfiguration(config);
+ } catch (Exception e) {
+ Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+ }
+ }
+
+ private void setLastReportedConfiguration(Configuration config) {
+ mLastReportedConfiguration.setTo(config);
+ }
+
+ Configuration getLastReportedConfiguration() {
+ return mLastReportedConfiguration;
}
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
@@ -574,6 +649,9 @@
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
}
+ pw.println(prefix + " Configuration=" + getConfiguration());
+ pw.println(prefix + " OverrideConfiguration=" + getOverrideConfiguration());
+ pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
}
}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingConstants.java b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
new file mode 100644
index 0000000..c2655a2
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appbinding;
+
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Constants that are configurable via the global settings for {@link AppBindingService}.
+ */
+class AppBindingConstants {
+ private static final String TAG = AppBindingService.TAG;
+
+ private static final String SERVICE_RECONNECT_BACKOFF_SEC_KEY =
+ "service_reconnect_backoff_sec";
+
+ private static final String SERVICE_RECONNECT_BACKOFF_INCREASE_KEY =
+ "service_reconnect_backoff_increase";
+
+ private static final String SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
+ "service_reconnect_max_backoff_sec";
+
+ public final String sourceSettings;
+
+ /**
+ * The back-off before re-connecting, when a service binding died, due to the app
+ * crashing repeatedly.
+ */
+ public final long SERVICE_RECONNECT_BACKOFF_SEC;
+
+ /**
+ * The exponential back-off increase factor when a binding dies multiple times.
+ */
+ public final double SERVICE_RECONNECT_BACKOFF_INCREASE;
+
+ /**
+ * The max back-off
+ */
+ public final long SERVICE_RECONNECT_MAX_BACKOFF_SEC;
+
+ private AppBindingConstants(String settings) {
+ sourceSettings = settings;
+
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(settings);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on
+ // with defaults.
+ Slog.e(TAG, "Bad setting: " + settings);
+ }
+
+ long serviceReconnectBackoffSec = parser.getLong(
+ SERVICE_RECONNECT_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+
+ double serviceReconnectBackoffIncrease = parser.getFloat(
+ SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);
+
+ long serviceReconnectMaxBackoffSec = parser.getLong(
+ SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
+
+ // Set minimum: 5 seconds.
+ serviceReconnectBackoffSec = Math.max(5, serviceReconnectBackoffSec);
+
+ // Set minimum: 1.0.
+ serviceReconnectBackoffIncrease =
+ Math.max(1, serviceReconnectBackoffIncrease);
+
+ // Make sure max >= default back off.
+ serviceReconnectMaxBackoffSec = Math.max(serviceReconnectBackoffSec,
+ serviceReconnectMaxBackoffSec);
+
+ SERVICE_RECONNECT_BACKOFF_SEC = serviceReconnectBackoffSec;
+ SERVICE_RECONNECT_BACKOFF_INCREASE = serviceReconnectBackoffIncrease;
+ SERVICE_RECONNECT_MAX_BACKOFF_SEC = serviceReconnectMaxBackoffSec;
+ }
+
+ /**
+ * Create a new instance from a settings string.
+ */
+ public static AppBindingConstants initializeFromString(String settings) {
+ return new AppBindingConstants(settings);
+ }
+
+ /**
+ * dumpsys support.
+ */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.println("Constants:");
+
+ pw.print(prefix);
+ pw.print(" SERVICE_RECONNECT_BACKOFF_SEC: ");
+ pw.println(SERVICE_RECONNECT_BACKOFF_SEC);
+
+ pw.print(prefix);
+ pw.print(" SERVICE_RECONNECT_BACKOFF_INCREASE: ");
+ pw.println(SERVICE_RECONNECT_BACKOFF_INCREASE);
+
+ pw.print(prefix);
+ pw.print(" SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
+ pw.println(SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ }
+}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
new file mode 100644
index 0000000..c5cb2a4
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appbinding;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import com.android.server.am.PersistentConnection;
+import com.android.server.appbinding.finders.AppServiceFinder;
+import com.android.server.appbinding.finders.SmsAppServiceFinder;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * System server that keeps a binding to an app to keep it always running.
+ *
+ * <p>As of android Q, we only use it for the default SMS app.
+ *
+ * TODO Unit tests
+ * TODO How do we handle force stop??
+ * TODO Change OOM adjustment to 200 or so
+ * TODO Only allow it when the service is associated with a secondary process.
+ */
+public class AppBindingService extends Binder {
+ public static final String TAG = "AppBindingService";
+
+ public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
+
+ private final Object mLock = new Object();
+
+ private final Injector mInjector;
+ private final Context mContext;
+ private final Handler mHandler;
+ private final IPackageManager mIPackageManager;
+
+ @GuardedBy("mLock")
+ private AppBindingConstants mConstants;
+
+ @GuardedBy("mLock")
+ private final SparseBooleanArray mRunningUsers = new SparseBooleanArray(2);
+
+ @GuardedBy("mLock")
+ private final ArrayList<AppServiceFinder> mApps = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private final ArrayList<AppServiceConnection> mConnections = new ArrayList<>();
+
+ static class Injector {
+ public IPackageManager getIPackageManager() {
+ return AppGlobals.getPackageManager();
+ }
+ }
+
+ /**
+ * {@link SystemService} for this service.
+ */
+ public static final class Lifecycle extends SystemService {
+ final AppBindingService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new AppBindingService(new Injector(), context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.APP_BINDING_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ mService.onBootPhase(phase);
+ }
+
+ @Override
+ public void onStartUser(int userHandle) {
+ mService.onStartUser(userHandle);
+ }
+
+ @Override
+ public void onUnlockUser(int userId) {
+ mService.onUnlockUser(userId);
+ }
+
+ @Override
+ public void onStopUser(int userHandle) {
+ mService.onStopUser(userHandle);
+ }
+ }
+
+ private AppBindingService(Injector injector, Context context) {
+ mInjector = injector;
+ mContext = context;
+
+ mIPackageManager = injector.getIPackageManager();
+
+ mHandler = BackgroundThread.getHandler();
+ mApps.add(new SmsAppServiceFinder(context, this::onAppChanged, mHandler));
+
+ // Initialize with the default value to make it non-null.
+ mConstants = AppBindingConstants.initializeFromString("");
+ }
+
+ private void forAllAppsLocked(Consumer<AppServiceFinder> consumer) {
+ for (int i = 0; i < mApps.size(); i++) {
+ consumer.accept(mApps.get(i));
+ }
+ }
+
+ private void onBootPhase(int phase) {
+ if (DEBUG) {
+ Slog.d(TAG, "onBootPhase: " + phase);
+ }
+ switch (phase) {
+ case SystemService.PHASE_ACTIVITY_MANAGER_READY:
+ onPhaseActivityManagerReady();
+ break;
+ case SystemService.PHASE_THIRD_PARTY_APPS_CAN_START:
+ onPhaseThirdPartyAppsCanStart();
+ break;
+ }
+ }
+
+ /**
+ * Handle boot phase PHASE_ACTIVITY_MANAGER_READY.
+ */
+ private void onPhaseActivityManagerReady() {
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ packageFilter.addDataScheme("package");
+
+ packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
+ packageFilter, null, mHandler);
+
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
+ userFilter, null, mHandler);
+
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.APP_BINDING_CONSTANTS), false, mSettingsObserver);
+
+ refreshConstants();
+ }
+
+ private final ContentObserver mSettingsObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ refreshConstants();
+ }
+ };
+
+ private void refreshConstants() {
+ final String newSetting = Settings.Global.getString(
+ mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS);
+
+ synchronized (mLock) {
+ if (TextUtils.equals(mConstants.sourceSettings, newSetting)) {
+ return;
+ }
+ Slog.i(TAG, "Updating constants with: " + newSetting);
+ mConstants = AppBindingConstants.initializeFromString(newSetting);
+
+ rebindAllLocked("settings update");
+ }
+ }
+
+ @VisibleForTesting
+ final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
+
+ final String action = intent.getAction();
+
+ if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
+ return;
+ }
+
+ final Uri intentUri = intent.getData();
+ final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
+ : null;
+ if (packageName == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
+ }
+
+ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+ switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ if (replacing) {
+ handlePackageAddedReplacing(packageName, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ if (!replacing) {
+ handlePackageRemoved(packageName, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ handlePackageChanged(packageName, userId);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Handle boot phase PHASE_THIRD_PARTY_APPS_CAN_START.
+ */
+ private void onPhaseThirdPartyAppsCanStart() {
+ synchronized (mLock) {
+ forAllAppsLocked(AppServiceFinder::startMonitoring);
+ }
+ }
+
+ /** User lifecycle callback. */
+ private void onStartUser(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onStartUser: u" + userId);
+ }
+ synchronized (mLock) {
+ mRunningUsers.append(userId, true);
+ bindServicesLocked(userId, null, "user start");
+ }
+ }
+
+ /** User lifecycle callback. */
+ private void onUnlockUser(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onUnlockUser: u" + userId);
+ }
+ synchronized (mLock) {
+ bindServicesLocked(userId, null, "user unlock");
+ }
+ }
+
+ /** User lifecycle callback. */
+ private void onStopUser(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onStopUser: u" + userId);
+ }
+ synchronized (mLock) {
+ unbindServicesLocked(userId, null, "user stop");
+
+ mRunningUsers.delete(userId);
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onUserRemoved: u" + userId);
+ }
+ synchronized (mLock) {
+ forAllAppsLocked((app) -> app.onUserRemoved(userId));
+
+ mRunningUsers.delete(userId);
+ }
+ }
+
+ /**
+ * Called when a target package changes; e.g. when the user changes the default SMS app.
+ */
+ private void onAppChanged(AppServiceFinder finder, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAppChanged: u" + userId + " " + finder.getAppDescription());
+ }
+ synchronized (mLock) {
+ final String reason = finder.getAppDescription() + " changed";
+ unbindServicesLocked(userId, finder, reason);
+ bindServicesLocked(userId, finder, reason);
+ }
+ }
+
+ @Nullable
+ private AppServiceFinder findFinderLocked(int userId, @NonNull String packageName) {
+ for (int i = 0; i < mApps.size(); i++) {
+ final AppServiceFinder app = mApps.get(i);
+ if (packageName.equals(app.getTargetPackage(userId))) {
+ return app;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private AppServiceConnection findConnectionLock(
+ int userId, @NonNull AppServiceFinder target) {
+ for (int i = 0; i < mConnections.size(); i++) {
+ final AppServiceConnection conn = mConnections.get(i);
+ if ((conn.getUserId() == userId) && (conn.getFinder() == target)) {
+ return conn;
+ }
+ }
+ return null;
+ }
+
+ private void handlePackageAddedReplacing(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handlePackageAddedReplacing: u" + userId + " " + packageName);
+ }
+ synchronized (mLock) {
+ final AppServiceFinder finder = findFinderLocked(userId, packageName);
+ if (finder != null) {
+ unbindServicesLocked(userId, finder, "package update");
+ bindServicesLocked(userId, finder, "package update");
+ }
+ }
+ }
+
+ private void handlePackageRemoved(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName);
+ }
+ synchronized (mLock) {
+ final AppServiceFinder finder = findFinderLocked(userId, packageName);
+ if (finder != null) {
+ unbindServicesLocked(userId, finder, "package uninstall");
+ }
+ }
+ }
+
+ private void handlePackageChanged(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName);
+ }
+ synchronized (mLock) {
+ final AppServiceFinder finder = findFinderLocked(userId, packageName);
+ if (finder != null) {
+ unbindServicesLocked(userId, finder, "package changed");
+ bindServicesLocked(userId, finder, "package changed");
+ }
+ }
+ }
+
+ private void rebindAllLocked(String reason) {
+ for (int i = 0; i < mRunningUsers.size(); i++) {
+ if (!mRunningUsers.valueAt(i)) {
+ continue;
+ }
+ final int userId = mRunningUsers.keyAt(i);
+
+ unbindServicesLocked(userId, null, reason);
+ bindServicesLocked(userId, null, reason);
+ }
+ }
+
+ private void bindServicesLocked(int userId, @Nullable AppServiceFinder target,
+ @NonNull String reasonForLog) {
+ for (int i = 0; i < mApps.size(); i++) {
+ final AppServiceFinder app = mApps.get(i);
+ if (target != null && target != app) {
+ continue;
+ }
+
+ // Disconnect from existing binding.
+ final AppServiceConnection existingConn = findConnectionLock(userId, app);
+ if (existingConn != null) {
+ unbindServicesLocked(userId, target, reasonForLog);
+ }
+
+ final ServiceInfo service = app.findService(userId, mIPackageManager);
+ if (service == null) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "bindServicesLocked: u" + userId + " " + app.getAppDescription()
+ + " binding " + service.getComponentName() + " for " + reasonForLog);
+ }
+ final AppServiceConnection conn =
+ new AppServiceConnection(mContext, userId, mConstants, mHandler,
+ app, service.getComponentName());
+ mConnections.add(conn);
+ conn.bind();
+ }
+ }
+
+ private void unbindServicesLocked(int userId, @Nullable AppServiceFinder target,
+ @NonNull String reasonForLog) {
+ for (int i = mConnections.size() - 1; i >= 0; i--) {
+ final AppServiceConnection conn = mConnections.get(i);
+ if ((conn.getUserId() != userId)
+ || (target != null && conn.getFinder() != target)) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "unbindServicesLocked: u" + userId
+ + " " + conn.getFinder().getAppDescription()
+ + " unbinding " + conn.getComponentName() + " for " + reasonForLog);
+ }
+ mConnections.remove(i);
+ conn.unbind();
+ }
+ }
+
+ private static class AppServiceConnection extends PersistentConnection<IInterface> {
+ private final AppBindingConstants mConstants;
+ private final AppServiceFinder mFinder;
+
+ AppServiceConnection(Context context, int userId, AppBindingConstants constants,
+ Handler handler, AppServiceFinder finder,
+ @NonNull ComponentName componentName) {
+ super(TAG, context, handler, userId, componentName,
+ constants.SERVICE_RECONNECT_BACKOFF_SEC,
+ constants.SERVICE_RECONNECT_BACKOFF_INCREASE,
+ constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ mFinder = finder;
+ mConstants = constants;
+ }
+
+ @Override
+ protected int getBindFlags() {
+ return Context.BIND_FOREGROUND_SERVICE;
+ }
+
+ @Override
+ protected IInterface asInterface(IBinder obj) {
+ return mFinder.asInterface(obj);
+ }
+
+ public AppServiceFinder getFinder() {
+ return mFinder;
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ if (args.length > 0 && "-s".equals(args[0])) {
+ dumpSimple(pw);
+ return;
+ }
+
+ synchronized (mLock) {
+ mConstants.dump(" ", pw);
+
+ pw.println();
+ pw.print(" Running users:");
+ for (int i = 0; i < mRunningUsers.size(); i++) {
+ if (mRunningUsers.valueAt(i)) {
+ pw.print(" ");
+ pw.print(mRunningUsers.keyAt(i));
+ }
+ }
+
+ pw.println();
+ pw.println(" Connections:");
+ for (int i = 0; i < mConnections.size(); i++) {
+ final AppServiceConnection conn = mConnections.get(i);
+ pw.print(" App type: ");
+ pw.print(conn.getFinder().getAppDescription());
+ pw.println();
+
+ conn.dump(" ", pw);
+ }
+ if (mConnections.size() == 0) {
+ pw.println(" None:");
+ }
+
+ pw.println();
+ pw.println(" Finders:");
+ forAllAppsLocked((app) -> app.dump(" ", pw));
+ }
+ }
+
+ /**
+ * Print simple output for CTS.
+ */
+ private void dumpSimple(PrintWriter pw) {
+ synchronized (mLock) {
+ for (int i = 0; i < mConnections.size(); i++) {
+ final AppServiceConnection conn = mConnections.get(i);
+
+ pw.print("conn,");
+ pw.print(conn.getFinder().getAppDescription());
+ pw.print(",");
+ pw.print(conn.getUserId());
+ pw.print(",");
+ pw.print(conn.getComponentName().getPackageName());
+ pw.print(",");
+ pw.print(conn.getComponentName().getClassName());
+ pw.print(",");
+ pw.print(conn.isBound() ? "bound" : "not-bound");
+ pw.print(",");
+ pw.print(conn.isConnected() ? "connected" : "not-connected");
+ pw.println();
+ }
+ forAllAppsLocked((app) -> app.dumpSimple(pw));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingUtils.java b/services/core/java/com/android/server/appbinding/AppBindingUtils.java
new file mode 100644
index 0000000..fcbaecf
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/AppBindingUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appbinding;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Utility class to find a persistent bound service within an app.
+ */
+public class AppBindingUtils {
+ private static final String TAG = "AppBindingUtils";
+ private AppBindingUtils() {
+ }
+
+ /**
+ * Find a service with the action {@code serviceAction} in the package {@code packageName}.
+ * Returns null in any of the following cases.
+ * - No service with the action is found.
+ * - More than 1 service with the action is found.
+ * - Found service is not protected with the permission {@code servicePermission}.
+ */
+ @Nullable
+ public static ServiceInfo findService(@NonNull String packageName, int userId,
+ String serviceAction, String servicePermission,
+ Class<?> serviceClassForLogging,
+ IPackageManager ipm,
+ StringBuilder errorMessage) {
+ final String simpleClassName = serviceClassForLogging.getSimpleName();
+ final Intent intent = new Intent(serviceAction);
+ intent.setPackage(packageName);
+
+ errorMessage.setLength(0); // Clear it.
+ try {
+ final ParceledListSlice<ResolveInfo> pls = ipm
+ .queryIntentServices(intent, null, /* flags=*/ 0, userId);
+ if (pls == null || pls.getList().size() == 0) {
+ errorMessage.append("Service with " + serviceAction + " not found.");
+ return null;
+ }
+ final List<ResolveInfo> list = pls.getList();
+ // Note if multiple services are found, that's an error, even if only one of them
+ // is exported.
+ if (list.size() > 1) {
+ errorMessage.append("More than one " + simpleClassName + "'s found in package "
+ + packageName + ". They'll all be ignored.");
+ Log.e(TAG, errorMessage.toString());
+ return null;
+ }
+ final ServiceInfo si = list.get(0).serviceInfo;
+
+ if (!servicePermission.equals(si.permission)) {
+ errorMessage.append(simpleClassName + " "
+ + si.getComponentName().flattenToShortString()
+ + " must be protected with " + servicePermission
+ + ".");
+ Log.e(TAG, errorMessage.toString());
+ return null;
+ }
+ return si;
+ } catch (RemoteException e) {
+ // Shouldn't happen
+ }
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
new file mode 100644
index 0000000..68c5e496
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.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 com.android.server.appbinding.finders;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.appbinding.AppBindingService;
+import com.android.server.appbinding.AppBindingUtils;
+
+import java.io.PrintWriter;
+import java.util.function.BiConsumer;
+
+/**
+ * Baseclss that finds "persistent" service from a type of an app.
+ *
+ * @param <TServiceType> Type of the target service class.
+ * @param <TServiceInterfaceType> Type of the IInterface class used by TServiceType.
+ */
+public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType extends IInterface> {
+ protected static final String TAG = AppBindingService.TAG;
+ protected static final boolean DEBUG = AppBindingService.DEBUG;
+
+ protected final Context mContext;
+ protected final BiConsumer<AppServiceFinder, Integer> mListener;
+ protected final Handler mHandler;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<String> mTargetPackages = new SparseArray(1);
+
+ @GuardedBy("mLock")
+ private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(1);
+
+ @GuardedBy("mLock")
+ private final SparseArray<String> mLastMessages = new SparseArray(1);
+
+ public AppServiceFinder(Context context,
+ BiConsumer<AppServiceFinder, Integer> listener,
+ Handler callbackHandler) {
+ mContext = context;
+ mListener = listener;
+ mHandler = callbackHandler;
+ }
+
+ /** Human readable description of the type of apps; e.g. [Default SMS app] */
+ @NonNull
+ public abstract String getAppDescription();
+
+ /** Start monitoring apps. (e.g. Start watching the default SMS app changes.) */
+ public void startMonitoring() {
+ }
+
+ /** Called when a user is removed. */
+ public void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mTargetPackages.delete(userId);
+ mTargetServices.delete(userId);
+ }
+ }
+
+ /**
+ * Find the target service from the target app on a given user.
+ */
+ @Nullable
+ public final ServiceInfo findService(int userId, IPackageManager ipm) {
+ synchronized (mLock) {
+ mTargetPackages.put(userId, null);
+ mTargetServices.put(userId, null);
+
+ final String targetPackage = getTargetPackage(userId);
+ if (DEBUG) {
+ Slog.d(TAG, getAppDescription() + " package=" + targetPackage);
+ }
+ if (targetPackage == null) {
+ final String message = "Target package not found";
+ mLastMessages.put(userId, message);
+ Slog.w(TAG, getAppDescription() + " u" + userId + " " + message);
+ return null;
+ }
+ mTargetPackages.put(userId, targetPackage);
+
+ final StringBuilder errorMessage = new StringBuilder();
+ final ServiceInfo service = AppBindingUtils.findService(
+ targetPackage,
+ userId,
+ getServiceAction(),
+ getServicePermission(),
+ getServiceClass(),
+ ipm,
+ errorMessage);
+
+ if (service == null) {
+ final String message = errorMessage.toString();
+ mLastMessages.put(userId, message);
+ if (DEBUG) {
+ Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
+ + " " + message);
+ }
+ return null;
+ }
+
+ final String message = "Valid service found";
+ mLastMessages.put(userId, message);
+ mTargetServices.put(userId, service);
+ return service;
+ }
+ }
+
+ protected abstract Class<TServiceType> getServiceClass();
+
+ /**
+ * Convert a binder reference to a service interface type.
+ */
+ public abstract TServiceInterfaceType asInterface(IBinder obj);
+
+ /**
+ * @return the target package on a given user.
+ */
+ @Nullable
+ public abstract String getTargetPackage(int userId);
+
+ /**
+ * @return the intent action that identifies the target service in the target app.
+ */
+ @NonNull
+ protected abstract String getServiceAction();
+
+ /**
+ * @return the permission that the target service must be protected with.
+ */
+ @NonNull
+ protected abstract String getServicePermission();
+
+ /** Dumpsys support. */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("App type: ");
+ pw.print(getAppDescription());
+ pw.println();
+
+ synchronized (mLock) {
+ for (int i = 0; i < mTargetPackages.size(); i++) {
+ pw.print(prefix);
+ pw.print(" User: ");
+ pw.print(mTargetPackages.keyAt(i));
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Package: ");
+ pw.print(mTargetPackages.valueAt(i));
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Service: ");
+ pw.print(mTargetServices.valueAt(i));
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Message: ");
+ pw.print(mLastMessages.valueAt(i));
+ pw.println();
+ }
+ }
+ }
+
+ /** Dumpys support */
+ public void dumpSimple(PrintWriter pw) {
+ synchronized (mLock) {
+ for (int i = 0; i < mTargetPackages.size(); i++) {
+ pw.print("finder,");
+ pw.print(getAppDescription());
+ pw.print(",");
+ pw.print(mTargetPackages.keyAt(i)); // User-id
+ pw.print(",");
+ pw.print(mTargetPackages.valueAt(i));
+ pw.print(",");
+ pw.print(mTargetServices.valueAt(i));
+ pw.print(",");
+ pw.print(mLastMessages.valueAt(i));
+ pw.println();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
new file mode 100644
index 0000000..c908bd9
--- /dev/null
+++ b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.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 com.android.server.appbinding.finders;
+
+import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
+
+import android.Manifest.permission;
+import android.app.ISmsAppService;
+import android.app.SmsAppService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.SmsApplication;
+
+import java.util.function.BiConsumer;
+
+/**
+ * Find the SmsAppService service within the default SMS app.
+ */
+public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsAppService> {
+ public SmsAppServiceFinder(Context context,
+ BiConsumer<AppServiceFinder, Integer> listener,
+ Handler callbackHandler) {
+ super(context, listener, callbackHandler);
+ }
+
+ @Override
+ public String getAppDescription() {
+ return "[Default SMS app]";
+ }
+
+ @Override
+ protected Class<SmsAppService> getServiceClass() {
+ return SmsAppService.class;
+ }
+
+ @Override
+ public ISmsAppService asInterface(IBinder obj) {
+ return ISmsAppService.Stub.asInterface(obj);
+ }
+
+ @Override
+ protected String getServiceAction() {
+ return TelephonyManager.ACTION_SMS_APP_SERVICE;
+ }
+
+ @Override
+ protected String getServicePermission() {
+ return permission.BIND_SMS_APP_SERVICE;
+ }
+
+ @Override
+ public String getTargetPackage(int userId) {
+ final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(
+ mContext, /* updateIfNeeded= */ true, userId);
+ return cn == null ? null : cn.getPackageName();
+ }
+
+ @Override
+ public void startMonitoring() {
+ final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter,
+ /* permission= */ null, mHandler);
+ }
+
+ private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
+ mListener.accept(SmsAppServiceFinder.this, getSendingUserId());
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8caa702..f0ff570 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -627,6 +627,13 @@
// If absolute volume is supported in AVRCP device
private boolean mAvrcpAbsVolSupported = false;
+ // Pre-scale for Bluetooth Absolute Volume
+ private float[] mPrescaleAbsoluteVolume = new float[] {
+ 0.5f, // Pre-scale for index 1
+ 0.7f, // Pre-scale for index 2
+ 0.85f, // Pre-scale for index 3
+ };
+
private static Long mLastDeviceConnectMsgTime = new Long(0);
private NotificationManager mNm;
@@ -878,6 +885,23 @@
mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
mRecordMonitor.initMonitor();
+
+ final float[] preScale = new float[3];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+ for (int i = 0; i < preScale.length; i++) {
+ if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
+ mPrescaleAbsoluteVolume[i] = preScale[i];
+ }
+ }
+
}
public void systemReady() {
@@ -4661,7 +4685,9 @@
@Override
public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
{
- Log.i(TAG, "setBluetoothHearingAidDeviceConnectionState");
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setHearingAidDeviceConnectionState state=" + state
+ + " addr=" + device.getAddress())).printLog(TAG));
setBluetoothHearingAidDeviceConnectionState(
device, state, false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
@@ -4699,12 +4725,12 @@
public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent, int a2dpVolume)
{
- mDeviceLogger.log(new AudioEventLogger.StringEvent(
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
"setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
// only querying address as this is the only readily available field on the device
+ " addr=" + device.getAddress()
+ " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
- + " vol=" + a2dpVolume));
+ + " vol=" + a2dpVolume)).printLog(TAG));
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
mDeviceLogger.log(new AudioEventLogger.StringEvent("A2DP connection state ignored"));
return 0;
@@ -4926,18 +4952,12 @@
if (index == 0) {
// 0% for volume 0
index = 0;
- } else if (index == 1) {
- // 50% for volume 1
- index = (int)(mIndexMax * 0.5) /10;
- } else if (index == 2) {
- // 70% for volume 2
- index = (int)(mIndexMax * 0.70) /10;
- } else if (index == 3) {
- // 85% for volume 3
- index = (int)(mIndexMax * 0.85) /10;
+ } else if (index > 0 && index <= 3) {
+ // Pre-scale for volume steps 1 2 and 3
+ index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10;
} else {
// otherwise, full gain
- index = (mIndexMax + 5)/10;
+ index = (mIndexMax + 5) / 10;
}
return index;
}
@@ -5870,6 +5890,8 @@
}
private void onSendBecomingNoisyIntent() {
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "broadcast ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
}
@@ -7235,7 +7257,7 @@
// - wired: logged before onSetWiredDeviceConnectionState() is executed
// - A2DP: logged at reception of method call
final private AudioEventLogger mDeviceLogger = new AudioEventLogger(
- LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP device connection");
+ LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection");
final private AudioEventLogger mForceUseLogger = new AudioEventLogger(
LOG_NB_EVENTS_FORCE_USE,
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 6b09f1b..aa4d34e 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -26,10 +26,13 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.security.KeyStore;
import android.util.Slog;
import com.android.internal.statusbar.IStatusBarService;
+import java.util.ArrayList;
+
/**
* A class to keep track of the authentication state for a given client.
*/
@@ -50,28 +53,68 @@
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 {
- mDialogReceiverFromClient.onDialogDismissed(reason);
+ 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(), "Unable to notify dialog dismissed", e);
+ Slog.e(getLogTag(), "Remote exception", e);
}
stop(true /* initiatedByClient */);
}
@@ -89,11 +132,18 @@
*/
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,
- BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
- BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
+ 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) {
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean requireConfirmation) {
super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
restricted, owner);
mOpId = opId;
@@ -101,6 +151,7 @@
mDialogReceiverFromClient = dialogReceiver;
mStatusBarService = statusBarService;
mHandler = new Handler(Looper.getMainLooper());
+ mRequireConfirmation = requireConfirmation;
}
@Override
@@ -154,9 +205,41 @@
return super.onError(deviceId, error, vendorCode);
}
+ 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);
+ }
+
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
+ if (authenticated) {
+ 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);
+ }
+ }
+
boolean result = false;
// If the biometric dialog is showing, notify authentication succeeded
@@ -173,7 +256,7 @@
}
}
- final BiometricService.ServiceListener listener = getListener();
+ final BiometricServiceBase.ServiceListener listener = getListener();
if (listener != null) {
try {
mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
@@ -184,15 +267,8 @@
Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
+ ", id=" + identifier.getBiometricId());
}
-
- // 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());
+ if (!mRequireConfirmation) {
+ notifyClientAuthenticationSucceeded(identifier);
}
}
} catch (RemoteException e) {
@@ -241,7 +317,8 @@
if (listener != null) {
vibrateSuccess();
}
- result |= true; // we have a valid biometric, done
+ // we have a valid biometric that doesn't require confirmation, done
+ result |= !mRequireConfirmation;
resetFailedAttempts();
onStop();
}
@@ -269,7 +346,7 @@
if (mBundle != null) {
try {
mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- getBiometricType());
+ getBiometricType(), mRequireConfirmation);
} catch (RemoteException e) {
Slog.e(getLogTag(), "Unable to show biometric dialog", e);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricPromptService.java b/services/core/java/com/android/server/biometrics/BiometricPromptService.java
deleted file mode 100644
index d1371d1..0000000
--- a/services/core/java/com/android/server/biometrics/BiometricPromptService.java
+++ /dev/null
@@ -1,345 +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.biometrics;
-
-import static android.Manifest.permission.USE_BIOMETRIC;
-import static android.Manifest.permission.USE_FINGERPRINT;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricPromptService;
-import android.hardware.biometrics.IBiometricPromptServiceReceiver;
-import android.hardware.face.FaceManager;
-import android.hardware.face.IFaceService;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintService;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.server.SystemService;
-
-import java.util.ArrayList;
-
-/**
- * System service that arbitrates the modality for BiometricPrompt to use.
- */
-public class BiometricPromptService extends SystemService {
-
- private static final String TAG = "BiometricPromptService";
-
- /**
- * 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
- };
-
- private final Handler mHandler;
- private final boolean mHasFeatureFingerprint;
- private final boolean mHasFeatureIris;
- private final boolean mHasFeatureFace;
-
- private IFingerprintService mFingerprintService;
- private IFaceService mFaceService;
-
- // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
- // polymorphism :/
- final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
-
- // Cache the current service that's being used. This is the service which
- // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
- // check (is caller the current client) is done in the <Biometric>Service.
- // Since Settings/System (not application) is responsible for changing preference, this
- // should be safe.
- private int mCurrentModality;
-
- private final class Authenticator {
- int mType;
- BiometricAuthenticator mAuthenticator;
-
- Authenticator(int type, BiometricAuthenticator authenticator) {
- mType = type;
- mAuthenticator = authenticator;
- }
-
- int getType() {
- return mType;
- }
-
- BiometricAuthenticator getAuthenticator() {
- return mAuthenticator;
- }
- }
-
- /**
- * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
- * should not carry any state. The reality is we need to keep a tiny amount of state so that
- * cancelAuthentication() can go to the right place.
- */
- private final class BiometricPromptServiceWrapper extends IBiometricPromptService.Stub {
-
- @Override // Binder call
- public void authenticate(IBinder token, long sessionId, int userId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
- // Check the USE_BIOMETRIC permission here. In the BiometricService, check do the
- // AppOps and foreground check.
- checkPermission();
-
- if (token == null || receiver == null || opPackageName == null || bundle == null
- || dialogReceiver == null) {
- Slog.e(TAG, "Unable to authenticate, one or more null arguments");
- return;
- }
-
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
-
- mHandler.post(() -> {
- mCurrentModality = checkAndGetBiometricModality(receiver);
-
- 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(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);
- }
- });
- }
-
- @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();
-
- 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");
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
- }
- });
- }
- }
-
- private void checkPermission() {
- if (getContext().checkCallingPermission(USE_FINGERPRINT)
- != PackageManager.PERMISSION_GRANTED) {
- getContext().enforceCallingPermission(USE_BIOMETRIC,
- "Must have USE_BIOMETRIC permission");
- }
- }
-
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public BiometricPromptService(Context context) {
- super(context);
-
- mHandler = new Handler(Looper.getMainLooper());
-
- final PackageManager pm = context.getPackageManager();
- mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
- mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
- mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
- }
-
- @Override
- public void onStart() {
- // TODO: maybe get these on-demand
- if (mHasFeatureFingerprint) {
- mFingerprintService = IFingerprintService.Stub.asInterface(
- ServiceManager.getService(Context.FINGERPRINT_SERVICE));
- }
- if (mHasFeatureFace) {
- mFaceService = IFaceService.Stub.asInterface(
- ServiceManager.getService(Context.FACE_SERVICE));
- }
-
- // Cache the authenticators
- for (int i = 0; i < FEATURE_ID.length; i++) {
- if (hasFeature(FEATURE_ID[i])) {
- Authenticator authenticator =
- new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
- mAuthenticators.add(authenticator);
- }
- }
-
- publishBinderService(Context.BIOMETRIC_PROMPT_SERVICE, new BiometricPromptServiceWrapper());
- }
-
- /**
- * Checks if there are any available biometrics, and returns the modality. This method also
- * 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.
- */
- private int checkAndGetBiometricModality(IBiometricPromptServiceReceiver receiver) {
- int modality = BIOMETRIC_NONE;
- final String hardwareUnavailable =
- getContext().getString(R.string.biometric_error_hw_unavailable);
-
- // No biometric features, send error
- if (mAuthenticators.isEmpty()) {
- try {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
- hardwareUnavailable);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
- }
-
- // Find first authenticator that's both detected and enrolled
- boolean isHardwareDetected = false;
- boolean hasTemplatesEnrolled = false;
- for (int i = 0; i < mAuthenticators.size(); i++) {
- int featureId = mAuthenticators.get(i).getType();
- BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
- if (authenticator.isHardwareDetected()) {
- isHardwareDetected = true;
- if (authenticator.hasEnrolledTemplates()) {
- hasTemplatesEnrolled = true;
- modality = featureId;
- break;
- }
- }
- }
-
- // Check error conditions
- if (!isHardwareDetected) {
- try {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- hardwareUnavailable);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
- }
- if (!hasTemplatesEnrolled) {
- try {
- receiver.onError(0 /* deviceId */,
- BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
- hardwareUnavailable);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return BIOMETRIC_NONE;
- }
-
- return modality;
- }
-
- private BiometricAuthenticator getAuthenticator(int type) {
- switch (type) {
- case BIOMETRIC_FINGERPRINT:
- return (FingerprintManager)
- getContext().getSystemService(Context.FINGERPRINT_SERVICE);
- case BIOMETRIC_IRIS:
- return null;
- case BIOMETRIC_FACE:
- return (FaceManager)
- getContext().getSystemService(Context.FACE_SERVICE);
- default:
- return null;
- }
- }
-
- private boolean hasFeature(int type) {
- switch (type) {
- case BIOMETRIC_FINGERPRINT:
- return mHasFeatureFingerprint;
- case BIOMETRIC_IRIS:
- return mHasFeatureIris;
- case BIOMETRIC_FACE:
- return mHasFeatureFace;
- default:
- return false;
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 73c4223..87cf9c4 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -11,508 +11,248 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.biometrics;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_FINGERPRINT;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
import android.app.AppOpsManager;
-import android.app.IActivityTaskManager;
-import android.app.PendingIntent;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
-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.fingerprint.Fingerprint;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
import android.os.Binder;
import android.os.Bundle;
-import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.IRemoteCallback;
-import android.os.PowerManager;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.security.KeyStore;
+import android.provider.Settings;
import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.R;
import com.android.server.SystemService;
-import com.android.server.biometrics.fingerprint.FingerprintService;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
/**
- * Abstract base class containing all of the business logic for biometric services, e.g.
- * Fingerprint, Face, Iris.
- *
- * @hide
+ * System service that arbitrates the modality for BiometricPrompt to use.
*/
-public abstract class BiometricService extends SystemService implements IHwBinder.DeathRecipient {
+public class BiometricService extends SystemService {
- protected static final boolean DEBUG = true;
+ private static final String TAG = "BiometricPromptService";
- private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
- private static final int MSG_USER_SWITCHING = 10;
- private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
- private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
+ /**
+ * 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;
- private final Context mContext;
- private final String mKeyguardPackage;
+ /**
+ * 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
+ };
+
private final AppOpsManager mAppOps;
- private final SparseBooleanArray mTimedLockoutCleared;
- private final SparseIntArray mFailedAttempts;
- private final IActivityTaskManager mActivityTaskManager;
- private final AlarmManager mAlarmManager;
- private final PowerManager mPowerManager;
- private final UserManager mUserManager;
- private final MetricsLogger mMetricsLogger;
- private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
- private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
- private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
- private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
+ private final Handler mHandler;
+ private final boolean mHasFeatureFingerprint;
+ private final boolean mHasFeatureIris;
+ private final boolean mHasFeatureFace;
- protected final IStatusBarService mStatusBarService;
- protected final Map<Integer, Long> mAuthenticatorIds =
- Collections.synchronizedMap(new HashMap<>());
- protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
- new ResetFailedAttemptsForUserRunnable();
- protected final H mHandler = new H();
+ private IFingerprintService mFingerprintService;
+ private IFaceService mFaceService;
- private ClientMonitor mCurrentClient;
- private ClientMonitor mPendingClient;
- private PerformanceStats mPerformanceStats;
- protected int mCurrentUserId = UserHandle.USER_NULL;
- // Tracks if the current authentication makes use of CryptoObjects.
- protected boolean mIsCrypto;
- // Normal authentications are tracked by mPerformanceMap.
- protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
- // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
- protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+ // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
+ // polymorphism :/
+ final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
- protected class PerformanceStats {
- public int accept; // number of accepted biometrics
- public int reject; // number of rejected biometrics
- public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
- // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
- public int lockout; // total number of lockouts
- public int permanentLockout; // total number of permanent lockouts
+ // Cache the current service that's being used. This is the service which
+ // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
+ // check (is caller the current client) is done in the <Biometric>Service.
+ // Since Settings/System (not application) is responsible for changing preference, this
+ // should be safe.
+ private int mCurrentModality;
+
+ private final class Authenticator {
+ int mType;
+ BiometricAuthenticator mAuthenticator;
+
+ Authenticator(int type, BiometricAuthenticator authenticator) {
+ mType = type;
+ mAuthenticator = authenticator;
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ BiometricAuthenticator getAuthenticator() {
+ return mAuthenticator;
+ }
}
/**
- * @return the log tag.
+ * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
+ * should not carry any state. The reality is we need to keep a tiny amount of state so that
+ * cancelAuthentication() can go to the right place.
*/
- protected abstract String getTag();
+ private final class BiometricPromptServiceWrapper extends IBiometricService.Stub {
- /**
- * @return the biometric utilities for a specific implementation.
- */
- protected abstract BiometricUtils getBiometricUtils();
+ @Override // Binder call
+ public void authenticate(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiver receiver, int flags, String opPackageName,
+ Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
+ // Check the USE_BIOMETRIC permission here. In the BiometricService, check do the
+ // AppOps and foreground check.
+ checkPermission();
- /**
- * @return the number of failed attempts after which the user will be temporarily locked out
- * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
- */
- protected abstract int getFailedAttemptsLockoutTimed();
-
- /**
- * @return the number of failed attempts after which the user will be permanently locked out
- * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
- */
- protected abstract int getFailedAttemptsLockoutPermanent();
-
- /**
- * @return the metrics constants for a biometric implementation.
- */
- protected abstract Metrics getMetrics();
-
- /**
- * @param userId
- * @return true if the enrollment limit has been reached.
- */
- protected abstract boolean hasReachedEnrollmentLimit(int userId);
-
- /**
- * Notifies the HAL that the user has changed.
- * @param userId
- * @param clientPackage
- */
- protected abstract void updateActiveGroup(int userId, String clientPackage);
-
- /**
- * @return The protected intent to reset lockout for a specific biometric.
- */
- protected abstract String getLockoutResetIntent();
-
- /**
- * @return The permission the sender is required to have in order for the lockout reset intent
- * to be received by the BiometricService implementation.
- */
- protected abstract String getLockoutBroadcastPermission();
-
- /**
- * @return The HAL ID.
- */
- protected abstract long getHalDeviceId();
-
- /**
- * This method is called when the user switches. Implementations should probably notify the
- * HAL.
- * @param userId
- */
- protected abstract void handleUserSwitching(int userId);
-
- /**
- * @param userId
- * @return Returns true if the user has any enrolled biometrics.
- */
- protected abstract boolean hasEnrolledBiometrics(int userId);
-
- /**
- * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
- * etc.
- */
- protected abstract String getManageBiometricPermission();
-
- /**
- * Checks if the caller has permission to use the biometric service - throws a SecurityException
- * if not.
- */
- protected abstract void checkUseBiometricPermission();
-
- /**
- * @return Returns one of the {@link AppOpsManager} constants which pertains to the specific
- * biometric service.
- */
- protected abstract int getAppOp();
-
-
- /**
- * Notifies clients of any change in the biometric state (active / idle). This is mainly for
- * Fingerprint navigation gestures.
- * @param isActive
- */
- protected void notifyClientActiveCallbacks(boolean isActive) {}
-
- protected abstract class AuthenticationClientImpl extends AuthenticationClient {
-
- 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) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener,
- targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService);
- }
-
- @Override
- public void onStart() {
- try {
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(getTag(), "Could not register task stack listener", e);
- }
- }
-
- @Override
- public void onStop() {
- try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(getTag(), "Could not unregister task stack listener", e);
- }
- }
-
- @Override
- public void resetFailedAttempts() {
- resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
-
- @Override
- public void notifyUserActivity() {
- userActivity();
- }
-
- @Override
- public int handleFailedAttempt() {
- final int currentUser = ActivityManager.getCurrentUser();
- mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
- mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
- final int lockoutMode = getLockoutMode();
- if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
- mPerformanceStats.permanentLockout++;
- } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
- mPerformanceStats.lockout++;
+ if (token == null || receiver == null || opPackageName == null || bundle == null
+ || dialogReceiver == null) {
+ Slog.e(TAG, "Unable to authenticate, one or more null arguments");
+ return;
}
- // Failing multiple times will continue to push out the lockout time
- if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- scheduleLockoutResetForUser(currentUser);
- return lockoutMode;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
- }
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
- protected class EnrollClientImpl extends EnrollClient {
+ mHandler.post(() -> {
+ mCurrentModality = checkAndGetBiometricModality(receiver);
- public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
- IBinder token, ServiceListener listener, int userId, int groupId,
- byte[] cryptoToken, boolean restricted, String owner) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener,
- userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
- }
-
- @Override
- public void notifyUserActivity() {
- userActivity();
- }
- }
-
- protected class RemovalClientImpl extends RemovalClient {
- private boolean mShouldNotify;
-
- public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
- IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
- boolean restricted, String owner) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
- userId, restricted, owner, getBiometricUtils());
- }
-
- public void setShouldNotifyUserActivity(boolean shouldNotify) {
- mShouldNotify = shouldNotify;
- }
-
- @Override
- public void notifyUserActivity() {
- if (mShouldNotify) {
- userActivity();
- }
- }
- }
-
- protected class EnumerateClientImpl extends EnumerateClient {
-
- public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
- IBinder token, ServiceListener listener, int groupId, int userId,
- boolean restricted, String owner) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
- restricted, owner);
- }
-
- @Override
- public void notifyUserActivity() {
- userActivity();
- }
- }
-
- /**
- * Wraps the callback interface from Service -> Manager
- */
- protected interface ServiceListener {
- default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) throws RemoteException {};
-
- void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
- throws RemoteException;
-
- void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId)
- throws RemoteException;
-
- void onAuthenticationFailed(long deviceId)
- throws RemoteException;
-
- void onError(long deviceId, int error, int vendorCode)
- throws RemoteException;
-
- default void onRemoved(BiometricAuthenticator.Identifier identifier,
- int remaining) throws RemoteException {};
-
- default void onEnumerated(BiometricAuthenticator.Identifier identifier,
- int remaining) throws RemoteException {};
- }
-
- /**
- * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
- * subclasses.
- */
- protected interface DaemonWrapper {
- int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
- int authenticate(long operationId, int groupId) throws RemoteException;
- int cancel() throws RemoteException;
- int remove(int groupId, int biometricId) throws RemoteException;
- int enumerate() throws RemoteException;
- int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
- }
-
- /**
- * Handler which all subclasses should post events to.
- */
- protected final class H extends Handler {
- @Override
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1);
- break;
-
- default:
- Slog.w(getTag(), "Unknown message:" + msg.what);
- }
- }
- }
-
- private final class BiometricTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskStackChanged() {
- try {
- if (!(mCurrentClient instanceof AuthenticationClient)) {
- return;
+ 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) {
+ // If the user disabled face for apps, return ERROR_HW_UNAVAILABLE
+ if (isFaceEnabledForApps()) {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ FaceManager.getErrorString(getContext(),
+ BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
+ } else {
+ 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);
}
- final String currentClient = mCurrentClient.getOwnerString();
- if (isKeyguard(currentClient)) {
- return; // Keyguard is always allowed
+ });
+ }
+
+ private boolean isFaceEnabledForApps() {
+ // TODO: maybe cache this and eliminate duplicated code with KeyguardUpdateMonitor
+ return Settings.Secure.getIntForUser(
+ getContext().getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_APP_ENABLED,
+ 1 /* default */,
+ UserHandle.USER_CURRENT) == 0;
+ }
+
+ @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();
+
+ 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");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
}
- List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(currentClient)) {
- Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
- + " currentClient: " + currentClient);
- mCurrentClient.stop(false /* initiatedByClient */);
+ });
+ }
+
+ @Override // Binder call
+ public boolean hasEnrolledBiometrics(String opPackageName) {
+ checkPermission();
+
+ if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, Binder.getCallingUid(),
+ opPackageName) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+ throw new SecurityException("Permission denied");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ boolean hasEnrolled = false;
+ try {
+ // Note: On devices with multi-modal authentication, the selection logic will need
+ // to be updated.
+ for (int i = 0; i < mAuthenticators.size(); i++) {
+ if (mAuthenticators.get(i).getAuthenticator().hasEnrolledTemplates()) {
+ hasEnrolled = true;
+ break;
}
}
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to get running tasks", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
+ return hasEnrolled;
}
}
- private final class ResetClientStateRunnable implements Runnable {
- @Override
- public void run() {
- /**
- * Warning: if we get here, the driver never confirmed our call to cancel the current
- * operation (authenticate, enroll, remove, enumerate, etc), which is
- * really bad. The result will be a 3-second delay in starting each new client.
- * If you see this on a device, make certain the driver notifies with
- * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
- * once it has successfully switched to the IDLE state in the HAL.
- * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
- * in response to an actual cancel() call.
- */
- Slog.w(getTag(), "Client "
- + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
- + " failed to respond to cancel, starting client "
- + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
-
- mCurrentClient = null;
- startClient(mPendingClient, false);
- }
- }
-
- private final class LockoutReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
- if (getLockoutResetIntent().equals(intent.getAction())) {
- final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
- resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
- }
- }
- }
-
- private final class ResetFailedAttemptsForUserRunnable implements Runnable {
- @Override
- public void run() {
- resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
- }
-
- private final class LockoutResetMonitor implements IBinder.DeathRecipient {
- private static final long WAKELOCK_TIMEOUT_MS = 2000;
- private final IBiometricServiceLockoutResetCallback mCallback;
- private final PowerManager.WakeLock mWakeLock;
-
- public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
- mCallback = callback;
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "lockout reset callback");
- try {
- mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
- } catch (RemoteException e) {
- Slog.w(getTag(), "caught remote exception in linkToDeath", e);
- }
- }
-
- public void sendLockoutReset() {
- if (mCallback != null) {
- try {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
- mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- releaseWakelock();
- }
- });
- } catch (DeadObjectException e) {
- Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
- mHandler.post(mRemoveCallbackRunnable);
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
- releaseWakelock();
- }
- }
- }
-
- private final Runnable mRemoveCallbackRunnable = new Runnable() {
- @Override
- public void run() {
- releaseWakelock();
- removeLockoutResetCallback(LockoutResetMonitor.this);
- }
- };
-
- @Override
- public void binderDied() {
- Slog.e(getTag(), "Lockout reset callback binder died");
- mHandler.post(mRemoveCallbackRunnable);
- }
-
- private void releaseWakelock() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
+ private void checkPermission() {
+ if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ != PackageManager.PERMISSION_GRANTED) {
+ getContext().enforceCallingPermission(USE_BIOMETRIC,
+ "Must have USE_BIOMETRIC permission");
}
}
@@ -527,589 +267,130 @@
*/
public BiometricService(Context context) {
super(context);
- mContext = context;
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
- com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+
mAppOps = context.getSystemService(AppOpsManager.class);
- mTimedLockoutCleared = new SparseBooleanArray();
- mFailedAttempts = new SparseIntArray();
- mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
- Context.ACTIVITY_TASK_SERVICE)).getService();
- mPowerManager = mContext.getSystemService(PowerManager.class);
- mAlarmManager = mContext.getSystemService(AlarmManager.class);
- mUserManager = UserManager.get(mContext);
- mMetricsLogger = new MetricsLogger();
- mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
- getLockoutBroadcastPermission(), null /* handler */);
+ mHandler = new Handler(Looper.getMainLooper());
+
+ final PackageManager pm = context.getPackageManager();
+ mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+ mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
+ mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
}
@Override
public void onStart() {
- listenForUserSwitches();
- }
+ // TODO: maybe get these on-demand
+ if (mHasFeatureFingerprint) {
+ mFingerprintService = IFingerprintService.Stub.asInterface(
+ ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+ }
+ if (mHasFeatureFace) {
+ mFaceService = IFaceService.Stub.asInterface(
+ ServiceManager.getService(Context.FACE_SERVICE));
+ }
- @Override
- public void serviceDied(long cookie) {
- Slog.e(getTag(), "HAL died");
- mMetricsLogger.count(getMetrics().tagHalDied(), 1);
- handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- 0 /*vendorCode */);
- }
+ // Cache the authenticators
+ for (int i = 0; i < FEATURE_ID.length; i++) {
+ if (hasFeature(FEATURE_ID[i])) {
+ Authenticator authenticator =
+ new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
+ mAuthenticators.add(authenticator);
+ }
+ }
- protected ClientMonitor getCurrentClient() {
- return mCurrentClient;
- }
-
- protected ClientMonitor getPendingClient() {
- return mPendingClient;
+ publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricPromptServiceWrapper());
}
/**
- * Callback handlers from the daemon. The caller must put this on a handler.
+ * Checks if there are any available biometrics, and returns the modality. This method also
+ * 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.
*/
+ private int checkAndGetBiometricModality(IBiometricServiceReceiver receiver) {
+ int modality = BIOMETRIC_NONE;
+ final String hardwareUnavailable =
+ getContext().getString(R.string.biometric_error_hw_unavailable);
- protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
- removeClient(client);
- }
- if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
- && client instanceof AuthenticationClient) {
- // ignore enrollment acquisitions or acquisitions when we're locked out
- mPerformanceStats.acquire++;
- }
- }
-
- protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
- ArrayList<Byte> token) {
- ClientMonitor client = mCurrentClient;
- final boolean authenticated = identifier.getBiometricId() != 0;
-
- if (authenticated) {
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
+ // No biometric features, send error
+ if (mAuthenticators.isEmpty()) {
+ try {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
+ hardwareUnavailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
}
- KeyStore.getInstance().addAuthToken(byteToken);
- }
- if (client != null && client.onAuthenticated(identifier, authenticated)) {
- removeClient(client);
- }
- if (authenticated) {
- mPerformanceStats.accept++;
- } else {
- mPerformanceStats.reject++;
- }
- }
-
- protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onEnrollResult(identifier, remaining)) {
- removeClient(client);
- // When enrollment finishes, update this group's authenticator id, as the HAL has
- // already generated a new authenticator id when the new biometric is enrolled.
- if (identifier instanceof Fingerprint) {
- updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
- } else {
- updateActiveGroup(mCurrentUserId, null);
- }
-
- }
- }
-
- protected void handleError(long deviceId, int error, int vendorCode) {
- final ClientMonitor client = mCurrentClient;
-
- if (DEBUG) Slog.v(getTag(), "handleError(client="
- + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
-
- if (client != null && client.onError(deviceId, error, vendorCode)) {
- removeClient(client);
+ return BIOMETRIC_NONE;
}
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mHandler.removeCallbacks(mResetClientState);
- if (mPendingClient != null) {
- if (DEBUG) Slog.v(getTag(), "start pending client " + mPendingClient.getOwnerString());
- startClient(mPendingClient, false);
- mPendingClient = null;
- }
- }
- }
-
- protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
- final int remaining) {
- if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
- + ", dev=" + identifier.getDeviceId()
- + ", rem=" + remaining);
-
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onRemoved(identifier, remaining)) {
- removeClient(client);
- // When the last biometric of a group is removed, update the authenticator id
- int userId = mCurrentUserId;
- if (identifier instanceof Fingerprint) {
- userId = ((Fingerprint) identifier).getGroupId();
- }
- if (!hasEnrolledBiometrics(userId)) {
- updateActiveGroup(userId, null);
- }
- }
- }
-
- /**
- * Calls from the Manager. These are still on the calling binder's thread.
- */
-
- protected void enrollInternal(EnrollClientImpl client, int userId) {
- if (hasReachedEnrollmentLimit(userId)) {
- return;
- }
-
- // Group ID is arbitrarily set to parent profile user ID. It just represents
- // the default biometrics for the user.
- if (!isCurrentUserOrProfile(userId)) {
- return;
- }
-
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void cancelEnrollmentInternal(IBinder token) {
- mHandler.post(() -> {
- ClientMonitor client = mCurrentClient;
- if (client instanceof EnrollClient && client.getToken() == token) {
- client.stop(client.getToken() == token);
- }
- });
- }
-
- protected void authenticateInternal(AuthenticationClientImpl client, long opId,
- String opPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
- }
-
- protected void authenticateInternal(AuthenticationClientImpl client, long opId,
- String opPackageName, int callingUid, int callingPid, int callingUserId) {
- if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
- return;
- }
-
- mHandler.post(() -> {
- mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
-
- // Get performance stats object for this user.
- HashMap<Integer, PerformanceStats> pmap
- = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
- PerformanceStats stats = pmap.get(mCurrentUserId);
- if (stats == null) {
- stats = new PerformanceStats();
- pmap.put(mCurrentUserId, stats);
- }
- mPerformanceStats = stats;
- mIsCrypto = (opId != 0);
-
- startAuthentication(client, opPackageName);
- });
- }
-
- protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
- }
-
- 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;
- }
-
- mHandler.post(() -> {
- ClientMonitor client = mCurrentClient;
- if (client instanceof AuthenticationClient) {
- if (client.getToken() == token) {
- if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
- client.stop(client.getToken() == token);
- } else {
- if (DEBUG) Slog.v(getTag(), "can't stop client "
- + client.getOwnerString() + " since tokens don't match");
- }
- } else if (client != null) {
- if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
- + client.getOwnerString());
- }
- });
- }
-
- protected void setActiveUserInternal(int userId) {
- mHandler.post(() -> {
- updateActiveGroup(userId, null /* clientPackage */);
- });
- }
-
- protected void removeInternal(RemovalClientImpl client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void enumerateInternal(EnumerateClientImpl client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- // Should be done on a handler thread - not on the Binder's thread.
- private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
- updateActiveGroup(client.getGroupId(), opPackageName);
-
- if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
-
- int lockoutMode = getLockoutMode();
- if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(getTag(), "In lockout mode(" + lockoutMode +
- ") ; disallowing authentication");
- int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
- if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
- Slog.w(getTag(), "Cannot send permanent lockout message to client");
- }
- return;
- }
- startClient(client, true /* initiatedByClient */);
- }
-
- protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
- mHandler.post(() -> {
- final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
- if (!mLockoutMonitors.contains(monitor)) {
- mLockoutMonitors.add(monitor);
- }
- });
- }
-
- /**
- * Helper methods.
- */
-
- /**
- * @param opPackageName name of package for caller
- * @param requireForeground only allow this call while app is in the foreground
- * @return true if caller can use the biometric API
- */
- protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
- int pid, int userId) {
- checkUseBiometricPermission();
-
- if (isKeyguard(opPackageName)) {
- return true; // Keyguard is always allowed
- }
- if (!isCurrentUserOrProfile(userId)) {
- Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
- return false;
- }
- if (mAppOps.noteOp(getAppOp(), uid, opPackageName) != AppOpsManager.MODE_ALLOWED) {
- Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
- return false;
- }
- if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
- opPackageName))) {
- Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
- return false;
- }
- return true;
- }
-
- /**
- * @param opPackageName package of the caller
- * @return true if this is the same client currently using the biometric
- */
- private boolean isCurrentClient(String opPackageName) {
- return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
- }
-
- /**
- * @return true if this is keyguard package
- */
- private boolean isKeyguard(String clientPackage) {
- return mKeyguardPackage.equals(clientPackage);
- }
-
- protected int getLockoutMode() {
- final int currentUser = ActivityManager.getCurrentUser();
- final int failedAttempts = mFailedAttempts.get(currentUser, 0);
- if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
- return AuthenticationClient.LOCKOUT_PERMANENT;
- } else if (failedAttempts > 0 &&
- mTimedLockoutCleared.get(currentUser, false) == false
- && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
- return AuthenticationClient.LOCKOUT_TIMED;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
-
- private boolean isForegroundActivity(int uid, int pid) {
- try {
- List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- int N = procs.size();
- for (int i = 0; i < N; i++) {
- ActivityManager.RunningAppProcessInfo proc = procs.get(i);
- if (proc.pid == pid && proc.uid == uid
- && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
- return true;
+ // Find first authenticator that's both detected and enrolled
+ boolean isHardwareDetected = false;
+ boolean hasTemplatesEnrolled = false;
+ for (int i = 0; i < mAuthenticators.size(); i++) {
+ int featureId = mAuthenticators.get(i).getType();
+ BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
+ if (authenticator.isHardwareDetected()) {
+ isHardwareDetected = true;
+ if (authenticator.hasEnrolledTemplates()) {
+ hasTemplatesEnrolled = true;
+ modality = featureId;
+ break;
}
}
- } catch (RemoteException e) {
- Slog.w(getTag(), "am.getRunningAppProcesses() failed");
}
- return false;
- }
- /**
- * Calls the HAL to switch states to the new task. If there's already a current task,
- * it calls cancel() and sets mPendingClient to begin when the current task finishes
- * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
- *
- * @param newClient the new client that wants to connect
- * @param initiatedByClient true for authenticate, remove and enroll
- */
- private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
- ClientMonitor currentClient = mCurrentClient;
- 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 ||
- currentClient instanceof FingerprintService.RemovalClientImpl) {
- // This condition means we're currently running internal diagnostics to
- // remove extra fingerprints in the hardware and/or the software
- // TODO: design an escape hatch in case client never finishes
- if (newClient != null) {
- Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- }
- } else {
- currentClient.stop(initiatedByClient);
+ // Check error conditions
+ if (!isHardwareDetected) {
+ try {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ hardwareUnavailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
}
- mPendingClient = newClient;
- 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);
-
- newClient.start();
+ return BIOMETRIC_NONE;
}
- }
-
- protected void removeClient(ClientMonitor client) {
- if (client != null) {
- client.destroy();
- if (client != mCurrentClient && mCurrentClient != null) {
- Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
- + mCurrentClient.getOwnerString());
+ if (!hasTemplatesEnrolled) {
+ try {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+ FaceManager.getErrorString(getContext(),
+ BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+ 0 /* vendorCode */));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
}
+ return BIOMETRIC_NONE;
}
- if (mCurrentClient != null) {
- if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
- mCurrentClient = null;
- }
- if (mPendingClient == null) {
- notifyClientActiveCallbacks(false);
+
+ return modality;
+ }
+
+ private BiometricAuthenticator getAuthenticator(int type) {
+ switch (type) {
+ case BIOMETRIC_FINGERPRINT:
+ return (FingerprintManager)
+ getContext().getSystemService(Context.FINGERPRINT_SERVICE);
+ case BIOMETRIC_IRIS:
+ return null;
+ case BIOMETRIC_FACE:
+ return (FaceManager)
+ getContext().getSystemService(Context.FACE_SERVICE);
+ default:
+ return null;
}
}
- /**
- * Populates existing authenticator ids. To be used only during the start of the service.
- */
- protected void loadAuthenticatorIds() {
- // This operation can be expensive, so keep track of the elapsed time. Might need to move to
- // background if it takes too long.
- long t = System.currentTimeMillis();
- mAuthenticatorIds.clear();
- for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
- int userId = getUserOrWorkProfileId(null, user.id);
- if (!mAuthenticatorIds.containsKey(userId)) {
- updateActiveGroup(userId, null);
- }
+ private boolean hasFeature(int type) {
+ switch (type) {
+ case BIOMETRIC_FINGERPRINT:
+ return mHasFeatureFingerprint;
+ case BIOMETRIC_IRIS:
+ return mHasFeatureIris;
+ case BIOMETRIC_FACE:
+ return mHasFeatureFace;
+ default:
+ return false;
}
-
- t = System.currentTimeMillis() - t;
- if (t > 1000) {
- Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
- }
- }
-
- /**
- * @param clientPackage the package of the caller
- * @return the profile id
- */
- protected int getUserOrWorkProfileId(String clientPackage, int userId) {
- if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
- return userId;
- }
- return getEffectiveUserId(userId);
- }
-
- protected boolean isRestricted() {
- // Only give privileged apps (like Settings) access to biometric info
- final boolean restricted = !hasPermission(getManageBiometricPermission());
- return restricted;
- }
-
- protected boolean hasPermission(String permission) {
- return getContext().checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- protected void checkPermission(String permission) {
- getContext().enforceCallingOrSelfPermission(permission,
- "Must have " + permission + " permission.");
- }
-
- protected boolean isCurrentUserOrProfile(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um == null) {
- Slog.e(getTag(), "Unable to acquire UserManager");
- return false;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- // Allow current user or profiles of the current user...
- for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
- if (profileId == userId) {
- return true;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- return false;
- }
-
- /***
- * @param opPackageName the name of the calling package
- * @return authenticator id for the calling user
- */
- protected long getAuthenticatorId(String opPackageName) {
- final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
- return mAuthenticatorIds.getOrDefault(userId, 0L);
- }
-
- private void scheduleLockoutResetForUser(int userId) {
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
- getLockoutResetIntentForUser(userId));
- }
-
- private PendingIntent getLockoutResetIntentForUser(int userId) {
- return PendingIntent.getBroadcast(mContext, userId,
- new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private void userActivity() {
- long now = SystemClock.uptimeMillis();
- mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
- }
-
- /**
- * @param userId
- * @return true if this is a work profile
- */
- private boolean isWorkProfile(int userId) {
- UserInfo userInfo = null;
- final long token = Binder.clearCallingIdentity();
- try {
- userInfo = mUserManager.getUserInfo(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return userInfo != null && userInfo.isManagedProfile();
- }
-
-
- private int getEffectiveUserId(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um != null) {
- final long callingIdentity = Binder.clearCallingIdentity();
- userId = um.getCredentialOwnerProfile(userId);
- Binder.restoreCallingIdentity(callingIdentity);
- } else {
- Slog.e(getTag(), "Unable to acquire UserManager");
- }
- return userId;
- }
-
- // Attempt counter should only be cleared when Keyguard goes away or when
- // a biometric is successfully authenticated.
- private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
- if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
- }
- if (clearAttemptCounter) {
- mFailedAttempts.put(userId, 0);
- }
- mTimedLockoutCleared.put(userId, true);
- // If we're asked to reset failed attempts externally (i.e. from Keyguard),
- // the alarm might still be pending; remove it.
- cancelLockoutResetForUser(userId);
- notifyLockoutResetMonitors();
- }
-
- private void cancelLockoutResetForUser(int userId) {
- mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
- }
-
- private void listenForUserSwitches() {
- try {
- ActivityManager.getService().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) throws RemoteException {
- mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
- .sendToTarget();
- }
- }, getTag());
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to listen for user switching event" ,e);
- }
- }
-
- private void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
- private void removeLockoutResetCallback(
- LockoutResetMonitor monitor) {
- mLockoutMonitors.remove(monitor);
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
new file mode 100644
index 0000000..6a22193
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -0,0 +1,1113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.biometrics;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+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.fingerprint.Fingerprint;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.SystemService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract base class containing all of the business logic for biometric services, e.g.
+ * Fingerprint, Face, Iris.
+ *
+ * @hide
+ */
+public abstract class BiometricServiceBase extends SystemService
+ implements IHwBinder.DeathRecipient {
+
+ protected static final boolean DEBUG = true;
+
+ private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
+ private static final int MSG_USER_SWITCHING = 10;
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+ private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
+
+ private final Context mContext;
+ private final String mKeyguardPackage;
+ private final SparseBooleanArray mTimedLockoutCleared;
+ private final SparseIntArray mFailedAttempts;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final AlarmManager mAlarmManager;
+ private final PowerManager mPowerManager;
+ private final UserManager mUserManager;
+ private final MetricsLogger mMetricsLogger;
+ private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
+ private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
+ private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+ private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
+
+ protected final IStatusBarService mStatusBarService;
+ protected final Map<Integer, Long> mAuthenticatorIds =
+ Collections.synchronizedMap(new HashMap<>());
+ protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
+ new ResetFailedAttemptsForUserRunnable();
+ protected final AppOpsManager mAppOps;
+ protected final H mHandler = new H();
+
+ private ClientMonitor mCurrentClient;
+ private ClientMonitor mPendingClient;
+ private PerformanceStats mPerformanceStats;
+ protected int mCurrentUserId = UserHandle.USER_NULL;
+ // Tracks if the current authentication makes use of CryptoObjects.
+ protected boolean mIsCrypto;
+ // Normal authentications are tracked by mPerformanceMap.
+ protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
+ // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+ protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+
+ protected class PerformanceStats {
+ public int accept; // number of accepted biometrics
+ public int reject; // number of rejected biometrics
+ public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
+ // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+ public int lockout; // total number of lockouts
+ public int permanentLockout; // total number of permanent lockouts
+ }
+
+ /**
+ * @return the log tag.
+ */
+ protected abstract String getTag();
+
+ /**
+ * @return the biometric utilities for a specific implementation.
+ */
+ protected abstract BiometricUtils getBiometricUtils();
+
+ /**
+ * @return the number of failed attempts after which the user will be temporarily locked out
+ * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+ */
+ protected abstract int getFailedAttemptsLockoutTimed();
+
+ /**
+ * @return the number of failed attempts after which the user will be permanently locked out
+ * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+ */
+ protected abstract int getFailedAttemptsLockoutPermanent();
+
+ /**
+ * @return the metrics constants for a biometric implementation.
+ */
+ protected abstract Metrics getMetrics();
+
+ /**
+ * @param userId
+ * @return true if the enrollment limit has been reached.
+ */
+ protected abstract boolean hasReachedEnrollmentLimit(int userId);
+
+ /**
+ * Notifies the HAL that the user has changed.
+ * @param userId
+ * @param clientPackage
+ */
+ protected abstract void updateActiveGroup(int userId, String clientPackage);
+
+ /**
+ * @return The protected intent to reset lockout for a specific biometric.
+ */
+ protected abstract String getLockoutResetIntent();
+
+ /**
+ * @return The permission the sender is required to have in order for the lockout reset intent
+ * to be received by the BiometricService implementation.
+ */
+ protected abstract String getLockoutBroadcastPermission();
+
+ /**
+ * @return The HAL ID.
+ */
+ protected abstract long getHalDeviceId();
+
+ /**
+ * This method is called when the user switches. Implementations should probably notify the
+ * HAL.
+ * @param userId
+ */
+ protected abstract void handleUserSwitching(int userId);
+
+ /**
+ * @param userId
+ * @return Returns true if the user has any enrolled biometrics.
+ */
+ protected abstract boolean hasEnrolledBiometrics(int userId);
+
+ /**
+ * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
+ * etc.
+ */
+ protected abstract String getManageBiometricPermission();
+
+ /**
+ * Checks if the caller has permission to use the biometric service - throws a SecurityException
+ * if not.
+ */
+ protected abstract void checkUseBiometricPermission();
+
+ /**
+ * Checks if the caller passes the app ops check
+ */
+ protected abstract boolean checkAppOps(int uid, String opPackageName);
+
+ /**
+ * Notifies clients of any change in the biometric state (active / idle). This is mainly for
+ * Fingerprint navigation gestures.
+ * @param isActive
+ */
+ protected void notifyClientActiveCallbacks(boolean isActive) {}
+
+ protected abstract class AuthenticationClientImpl extends AuthenticationClient {
+
+ 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);
+ }
+
+ @Override
+ public void onStart() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Could not register task stack listener", e);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Could not unregister task stack listener", e);
+ }
+ }
+
+ @Override
+ public void resetFailedAttempts() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+
+ @Override
+ public int handleFailedAttempt() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+ mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+ final int lockoutMode = getLockoutMode();
+ if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+ mPerformanceStats.permanentLockout++;
+ } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+ mPerformanceStats.lockout++;
+ }
+
+ // Failing multiple times will continue to push out the lockout time
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ scheduleLockoutResetForUser(currentUser);
+ return lockoutMode;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
+ @Override
+ public void onAuthenticationConfirmed() {
+ removeClient(mCurrentClient);
+ }
+ }
+
+ protected class EnrollClientImpl extends EnrollClient {
+
+ public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int userId, int groupId,
+ byte[] cryptoToken, boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener,
+ userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+ }
+
+ protected class RemovalClientImpl extends RemovalClient {
+ private boolean mShouldNotify;
+
+ public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+ boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+ userId, restricted, owner, getBiometricUtils());
+ }
+
+ public void setShouldNotifyUserActivity(boolean shouldNotify) {
+ mShouldNotify = shouldNotify;
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ if (mShouldNotify) {
+ userActivity();
+ }
+ }
+ }
+
+ protected class EnumerateClientImpl extends EnumerateClient {
+
+ public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int groupId, int userId,
+ boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
+ restricted, owner);
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+ }
+
+ /**
+ * Wraps the callback interface from Service -> Manager
+ */
+ protected interface ServiceListener {
+ default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {};
+
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException;
+
+ void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId)
+ throws RemoteException;
+
+ void onAuthenticationFailed(long deviceId)
+ throws RemoteException;
+
+ void onError(long deviceId, int error, int vendorCode)
+ throws RemoteException;
+
+ default void onRemoved(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {};
+
+ default void onEnumerated(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {};
+ }
+
+ /**
+ * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
+ * subclasses.
+ */
+ protected interface DaemonWrapper {
+ int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+ int authenticate(long operationId, int groupId) throws RemoteException;
+ int cancel() throws RemoteException;
+ int remove(int groupId, int biometricId) throws RemoteException;
+ int enumerate() throws RemoteException;
+ int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
+ }
+
+ /**
+ * Handler which all subclasses should post events to.
+ */
+ protected final class H extends Handler {
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_USER_SWITCHING:
+ handleUserSwitching(msg.arg1);
+ break;
+
+ default:
+ Slog.w(getTag(), "Unknown message:" + msg.what);
+ }
+ }
+ }
+
+ private final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ if (!(mCurrentClient instanceof AuthenticationClient)) {
+ return;
+ }
+ final String currentClient = mCurrentClient.getOwnerString();
+ if (isKeyguard(currentClient)) {
+ return; // Keyguard is always allowed
+ }
+ List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(currentClient)) {
+ Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
+ + " currentClient: " + currentClient);
+ mCurrentClient.stop(false /* initiatedByClient */);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to get running tasks", e);
+ }
+ }
+ }
+
+ private final class ResetClientStateRunnable implements Runnable {
+ @Override
+ public void run() {
+ /**
+ * Warning: if we get here, the driver never confirmed our call to cancel the current
+ * operation (authenticate, enroll, remove, enumerate, etc), which is
+ * really bad. The result will be a 3-second delay in starting each new client.
+ * If you see this on a device, make certain the driver notifies with
+ * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
+ * once it has successfully switched to the IDLE state in the HAL.
+ * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
+ * in response to an actual cancel() call.
+ */
+ Slog.w(getTag(), "Client "
+ + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+ + " failed to respond to cancel, starting client "
+ + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+
+ mCurrentClient = null;
+ startClient(mPendingClient, false);
+ }
+ }
+
+ private final class LockoutReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
+ if (getLockoutResetIntent().equals(intent.getAction())) {
+ final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+ resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+ }
+ }
+ }
+
+ private final class ResetFailedAttemptsForUserRunnable implements Runnable {
+ @Override
+ public void run() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+ }
+
+ private final class LockoutResetMonitor implements IBinder.DeathRecipient {
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+ private final IBiometricServiceLockoutResetCallback mCallback;
+ private final PowerManager.WakeLock mWakeLock;
+
+ public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
+ mCallback = callback;
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "lockout reset callback");
+ try {
+ mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "caught remote exception in linkToDeath", e);
+ }
+ }
+
+ public void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ releaseWakelock();
+ }
+ });
+ } catch (DeadObjectException e) {
+ Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
+ mHandler.post(mRemoveCallbackRunnable);
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
+ releaseWakelock();
+ }
+ }
+ }
+
+ private final Runnable mRemoveCallbackRunnable = new Runnable() {
+ @Override
+ public void run() {
+ releaseWakelock();
+ removeLockoutResetCallback(LockoutResetMonitor.this);
+ }
+ };
+
+ @Override
+ public void binderDied() {
+ Slog.e(getTag(), "Lockout reset callback binder died");
+ mHandler.post(mRemoveCallbackRunnable);
+ }
+
+ private void releaseWakelock() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public BiometricServiceBase(Context context) {
+ super(context);
+ mContext = context;
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mTimedLockoutCleared = new SparseBooleanArray();
+ mFailedAttempts = new SparseIntArray();
+ mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
+ Context.ACTIVITY_TASK_SERVICE)).getService();
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mUserManager = UserManager.get(mContext);
+ mMetricsLogger = new MetricsLogger();
+ mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+ getLockoutBroadcastPermission(), null /* handler */);
+ }
+
+ @Override
+ public void onStart() {
+ listenForUserSwitches();
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.e(getTag(), "HAL died");
+ mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+ handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /*vendorCode */);
+ }
+
+ protected ClientMonitor getCurrentClient() {
+ return mCurrentClient;
+ }
+
+ protected ClientMonitor getPendingClient() {
+ return mPendingClient;
+ }
+
+ /**
+ * Callback handlers from the daemon. The caller must put this on a handler.
+ */
+
+ protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
+ removeClient(client);
+ }
+ if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+ && client instanceof AuthenticationClient) {
+ // ignore enrollment acquisitions or acquisitions when we're locked out
+ mPerformanceStats.acquire++;
+ }
+ }
+
+ protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
+ ArrayList<Byte> token) {
+ ClientMonitor client = mCurrentClient;
+ final boolean authenticated = identifier.getBiometricId() != 0;
+
+ if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
+ removeClient(client);
+ }
+ if (authenticated) {
+ mPerformanceStats.accept++;
+ } else {
+ mPerformanceStats.reject++;
+ }
+ }
+
+ protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onEnrollResult(identifier, remaining)) {
+ removeClient(client);
+ // When enrollment finishes, update this group's authenticator id, as the HAL has
+ // already generated a new authenticator id when the new biometric is enrolled.
+ if (identifier instanceof Fingerprint) {
+ updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
+ } else {
+ updateActiveGroup(mCurrentUserId, null);
+ }
+
+ }
+ }
+
+ protected void handleError(long deviceId, int error, int vendorCode) {
+ final ClientMonitor client = mCurrentClient;
+
+ if (DEBUG) Slog.v(getTag(), "handleError(client="
+ + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+
+ if (client != null && client.onError(deviceId, error, vendorCode)) {
+ removeClient(client);
+ }
+
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mHandler.removeCallbacks(mResetClientState);
+ if (mPendingClient != null) {
+ if (DEBUG) Slog.v(getTag(), "start pending client " +
+ mPendingClient.getOwnerString());
+ startClient(mPendingClient, false);
+ mPendingClient = null;
+ }
+ }
+ }
+
+ protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
+ final int remaining) {
+ if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
+ + ", dev=" + identifier.getDeviceId()
+ + ", rem=" + remaining);
+
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onRemoved(identifier, remaining)) {
+ removeClient(client);
+ // When the last biometric of a group is removed, update the authenticator id
+ int userId = mCurrentUserId;
+ if (identifier instanceof Fingerprint) {
+ userId = ((Fingerprint) identifier).getGroupId();
+ }
+ if (!hasEnrolledBiometrics(userId)) {
+ updateActiveGroup(userId, null);
+ }
+ }
+ }
+
+ /**
+ * Calls from the Manager. These are still on the calling binder's thread.
+ */
+
+ protected void enrollInternal(EnrollClientImpl client, int userId) {
+ if (hasReachedEnrollmentLimit(userId)) {
+ return;
+ }
+
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default biometrics for the user.
+ if (!isCurrentUserOrProfile(userId)) {
+ return;
+ }
+
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ protected void cancelEnrollmentInternal(IBinder token) {
+ mHandler.post(() -> {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof EnrollClient && client.getToken() == token) {
+ client.stop(client.getToken() == token);
+ }
+ });
+ }
+
+ protected void authenticateInternal(AuthenticationClientImpl client, long opId,
+ String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
+ }
+
+ protected void authenticateInternal(AuthenticationClientImpl client, long opId,
+ String opPackageName, int callingUid, int callingPid, int callingUserId) {
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
+ return;
+ }
+
+ mHandler.post(() -> {
+ mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
+
+ // Get performance stats object for this user.
+ HashMap<Integer, PerformanceStats> pmap
+ = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+ PerformanceStats stats = pmap.get(mCurrentUserId);
+ if (stats == null) {
+ stats = new PerformanceStats();
+ pmap.put(mCurrentUserId, stats);
+ }
+ mPerformanceStats = stats;
+ mIsCrypto = (opId != 0);
+
+ startAuthentication(client, opPackageName);
+ });
+ }
+
+ protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
+ }
+
+ 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;
+ }
+
+ mHandler.post(() -> {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof AuthenticationClient) {
+ if (client.getToken() == token) {
+ if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+ client.stop(client.getToken() == token);
+ } else {
+ if (DEBUG) Slog.v(getTag(), "can't stop client "
+ + client.getOwnerString() + " since tokens don't match");
+ }
+ } else if (client != null) {
+ if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+ + client.getOwnerString());
+ }
+ });
+ }
+
+ protected void setActiveUserInternal(int userId) {
+ mHandler.post(() -> {
+ updateActiveGroup(userId, null /* clientPackage */);
+ });
+ }
+
+ protected void removeInternal(RemovalClientImpl client) {
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ protected void enumerateInternal(EnumerateClientImpl client) {
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ // Should be done on a handler thread - not on the Binder's thread.
+ private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
+ updateActiveGroup(client.getGroupId(), opPackageName);
+
+ if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
+
+ int lockoutMode = getLockoutMode();
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "In lockout mode(" + lockoutMode +
+ ") ; disallowing authentication");
+ int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
+ Slog.w(getTag(), "Cannot send permanent lockout message to client");
+ }
+ return;
+ }
+ startClient(client, true /* initiatedByClient */);
+ }
+
+ protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
+ mHandler.post(() -> {
+ final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
+ if (!mLockoutMonitors.contains(monitor)) {
+ mLockoutMonitors.add(monitor);
+ }
+ });
+ }
+
+ /**
+ * Helper methods.
+ */
+
+ /**
+ * @param opPackageName name of package for caller
+ * @param requireForeground only allow this call while app is in the foreground
+ * @return true if caller can use the biometric API
+ */
+ protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
+ int pid, int userId) {
+ checkUseBiometricPermission();
+
+ if (isKeyguard(opPackageName)) {
+ return true; // Keyguard is always allowed
+ }
+ if (!isCurrentUserOrProfile(userId)) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
+ return false;
+ }
+ if (!checkAppOps(uid, opPackageName)) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
+ return false;
+ }
+
+ if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
+ opPackageName))) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param opPackageName package of the caller
+ * @return true if this is the same client currently using the biometric
+ */
+ private boolean isCurrentClient(String opPackageName) {
+ return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+ }
+
+ /**
+ * @return true if this is keyguard package
+ */
+ private boolean isKeyguard(String clientPackage) {
+ return mKeyguardPackage.equals(clientPackage);
+ }
+
+ protected int getLockoutMode() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+ if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
+ return AuthenticationClient.LOCKOUT_PERMANENT;
+ } else if (failedAttempts > 0 &&
+ mTimedLockoutCleared.get(currentUser, false) == false
+ && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
+ return AuthenticationClient.LOCKOUT_TIMED;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
+ private boolean isForegroundActivity(int uid, int pid) {
+ try {
+ List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == pid && proc.uid == uid
+ && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
+
+ /**
+ * Calls the HAL to switch states to the new task. If there's already a current task,
+ * it calls cancel() and sets mPendingClient to begin when the current task finishes
+ * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
+ *
+ * @param newClient the new client that wants to connect
+ * @param initiatedByClient true for authenticate, remove and enroll
+ */
+ private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+ ClientMonitor currentClient = mCurrentClient;
+ 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 ||
+ currentClient instanceof FingerprintService.RemovalClientImpl) {
+ // This condition means we're currently running internal diagnostics to
+ // remove extra fingerprints in the hardware and/or the software
+ // TODO: design an escape hatch in case client never finishes
+ if (newClient != null) {
+ Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ }
+ } else {
+ currentClient.stop(initiatedByClient);
+ }
+ mPendingClient = newClient;
+ 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);
+
+ newClient.start();
+ }
+ }
+
+ protected void removeClient(ClientMonitor client) {
+ if (client != null) {
+ client.destroy();
+ if (client != mCurrentClient && mCurrentClient != null) {
+ Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
+ + mCurrentClient.getOwnerString());
+ }
+ }
+ if (mCurrentClient != null) {
+ if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
+ mCurrentClient = null;
+ }
+ if (mPendingClient == null) {
+ notifyClientActiveCallbacks(false);
+ }
+ }
+
+ /**
+ * Populates existing authenticator ids. To be used only during the start of the service.
+ */
+ protected void loadAuthenticatorIds() {
+ // This operation can be expensive, so keep track of the elapsed time. Might need to move to
+ // background if it takes too long.
+ long t = System.currentTimeMillis();
+ mAuthenticatorIds.clear();
+ for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
+ int userId = getUserOrWorkProfileId(null, user.id);
+ if (!mAuthenticatorIds.containsKey(userId)) {
+ updateActiveGroup(userId, null);
+ }
+ }
+
+ t = System.currentTimeMillis() - t;
+ if (t > 1000) {
+ Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
+ }
+ }
+
+ /**
+ * @param clientPackage the package of the caller
+ * @return the profile id
+ */
+ protected int getUserOrWorkProfileId(String clientPackage, int userId) {
+ if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+ return userId;
+ }
+ return getEffectiveUserId(userId);
+ }
+
+ protected boolean isRestricted() {
+ // Only give privileged apps (like Settings) access to biometric info
+ final boolean restricted = !hasPermission(getManageBiometricPermission());
+ return restricted;
+ }
+
+ protected boolean hasPermission(String permission) {
+ return getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ protected void checkPermission(String permission) {
+ getContext().enforceCallingOrSelfPermission(permission,
+ "Must have " + permission + " permission.");
+ }
+
+ protected boolean isCurrentUserOrProfile(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um == null) {
+ Slog.e(getTag(), "Unable to acquire UserManager");
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Allow current user or profiles of the current user...
+ for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+ if (profileId == userId) {
+ return true;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return false;
+ }
+
+ /***
+ * @param opPackageName the name of the calling package
+ * @return authenticator id for the calling user
+ */
+ protected long getAuthenticatorId(String opPackageName) {
+ final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
+ return mAuthenticatorIds.getOrDefault(userId, 0L);
+ }
+
+ private void scheduleLockoutResetForUser(int userId) {
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ getLockoutResetIntentForUser(userId));
+ }
+
+ private PendingIntent getLockoutResetIntentForUser(int userId) {
+ return PendingIntent.getBroadcast(mContext, userId,
+ new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private void userActivity() {
+ long now = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ }
+
+ /**
+ * @param userId
+ * @return true if this is a work profile
+ */
+ private boolean isWorkProfile(int userId) {
+ UserInfo userInfo = null;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ userInfo = mUserManager.getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return userInfo != null && userInfo.isManagedProfile();
+ }
+
+
+ private int getEffectiveUserId(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um != null) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ userId = um.getCredentialOwnerProfile(userId);
+ Binder.restoreCallingIdentity(callingIdentity);
+ } else {
+ Slog.e(getTag(), "Unable to acquire UserManager");
+ }
+ return userId;
+ }
+
+ // Attempt counter should only be cleared when Keyguard goes away or when
+ // a biometric is successfully authenticated.
+ private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+ if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+ }
+ if (clearAttemptCounter) {
+ mFailedAttempts.put(userId, 0);
+ }
+ mTimedLockoutCleared.put(userId, true);
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+ // the alarm might still be pending; remove it.
+ cancelLockoutResetForUser(userId);
+ notifyLockoutResetMonitors();
+ }
+
+ private void cancelLockoutResetForUser(int userId) {
+ mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+ }
+
+ private void listenForUserSwitches() {
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) throws RemoteException {
+ mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+ .sendToTarget();
+ }
+ }, getTag());
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "Failed to listen for user switching event" ,e);
+ }
+ }
+
+ private void notifyLockoutResetMonitors() {
+ for (int i = 0; i < mLockoutMonitors.size(); i++) {
+ mLockoutMonitors.get(i).sendLockoutReset();
+ }
+ }
+
+ private void removeLockoutResetCallback(
+ LockoutResetMonitor monitor) {
+ mLockoutMonitors.remove(monitor);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index d30bed2..d1daad5 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -28,6 +28,7 @@
import com.android.internal.logging.MetricsLogger;
+import java.util.ArrayList;
import java.util.NoSuchElementException;
/**
@@ -37,7 +38,7 @@
*/
public abstract class ClientMonitor implements IBinder.DeathRecipient {
protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
- protected static final boolean DEBUG = BiometricService.DEBUG;
+ protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -53,10 +54,10 @@
private final String mOwner;
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
- private final BiometricService.DaemonWrapper mDaemon;
+ private final BiometricServiceBase.DaemonWrapper mDaemon;
private IBinder mToken;
- private BiometricService.ServiceListener mListener;
+ private BiometricServiceBase.ServiceListener mListener;
protected final MetricsLogger mMetricsLogger;
protected final Metrics mMetrics;
@@ -75,9 +76,10 @@
* permission
* @param owner name of the client that owns this
*/
- public ClientMonitor(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
- long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
- int groupId, boolean restricted, String owner) {
+ public ClientMonitor(Context context, Metrics metrics,
+ BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
+ BiometricServiceBase.ServiceListener listener, int userId, int groupId,
+ boolean restricted, String owner) {
mContext = context;
mMetrics = metrics;
mDaemon = daemon;
@@ -128,7 +130,7 @@
public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining);
public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated);
+ boolean authenticated, ArrayList<Byte> token);
public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining);
public abstract boolean onEnumerationResult(
@@ -220,11 +222,11 @@
return mOwner;
}
- public final BiometricService.ServiceListener getListener() {
+ public final BiometricServiceBase.ServiceListener getListener() {
return mListener;
}
- public final BiometricService.DaemonWrapper getDaemonWrapper() {
+ public final BiometricServiceBase.DaemonWrapper getDaemonWrapper() {
return mDaemon;
}
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index c305eca6..76dc5a9 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -34,10 +35,10 @@
private final byte[] mCryptoToken;
private final BiometricUtils mBiometricUtils;
- public EnrollClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
- long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
- int groupId, byte[] cryptoToken, boolean restricted, String owner,
- BiometricUtils utils) {
+ public EnrollClient(Context context, Metrics metrics,
+ BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
+ BiometricServiceBase.ServiceListener listener, int userId, int groupId,
+ byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner);
mBiometricUtils = utils;
@@ -126,7 +127,7 @@
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enroll!");
return true; // Invalid for EnrollClient
}
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index b763769..47dc7ff 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -23,13 +23,16 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
+
/**
* A class to keep track of the enumeration state for a given client.
*/
public abstract class EnumerateClient extends ClientMonitor {
- public EnumerateClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
- long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int groupId,
- int userId, boolean restricted, String owner) {
+ public EnumerateClient(Context context, Metrics metrics,
+ BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
+ BiometricServiceBase.ServiceListener listener, int groupId, int userId,
+ boolean restricted, String owner) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
owner);
}
@@ -94,7 +97,7 @@
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enumerate!");
return true; // Invalid for Enumerate.
}
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 3bf7f04..15b3773 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -23,6 +23,8 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
+
/**
* A class to keep track of the remove state for a given client.
*/
@@ -30,10 +32,10 @@
private final int mBiometricId;
private final BiometricUtils mBiometricUtils;
- public RemovalClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
- long halDeviceId, IBinder token, BiometricService.ServiceListener listener,
- int biometricId, int groupId, int userId, boolean restricted, String owner,
- BiometricUtils utils) {
+ public RemovalClient(Context context, Metrics metrics,
+ BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
+ 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);
mBiometricId = biometricId;
@@ -111,7 +113,7 @@
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for remove!");
return true; // Invalid for Remove.
}
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 660710e..f6af52a 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -28,10 +28,11 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricPromptServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
@@ -52,7 +53,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
-import com.android.server.biometrics.BiometricService;
+import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.Metrics;
@@ -73,7 +74,7 @@
*
* @hide
*/
-public class FaceService extends BiometricService {
+public class FaceService extends BiometricServiceBase {
protected static final String TAG = "FaceService";
private static final boolean DEBUG = true;
@@ -82,16 +83,18 @@
"com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
+ private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
private final class FaceAuthClient extends AuthenticationClientImpl {
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) {
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted,
- owner, bundle, dialogReceiver, statusBarService);
+ restricted, owner, bundle, dialogReceiver, statusBarService,
+ requireConfirmation);
}
@Override
@@ -119,15 +122,15 @@
* The following methods contain common code which is shared in biometrics/common.
*/
@Override // Binder call
- public long preEnroll(IBinder token) {
+ public long generateChallenge(IBinder token) {
checkPermission(MANAGE_BIOMETRIC);
- return startPreEnroll(token);
+ return startGenerateChallenge(token);
}
@Override // Binder call
- public int postEnroll(IBinder token) {
+ public int revokeChallenge(IBinder token) {
checkPermission(MANAGE_BIOMETRIC);
- return startPostEnroll(token);
+ return startRevokeChallenge(token);
}
@Override // Binder call
@@ -159,15 +162,15 @@
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- null /* bundle */, null /* dialogReceiver */, mStatusBarService);
-
+ null /* bundle */, null /* dialogReceiver */, mStatusBarService,
+ false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(IBinder token, long opId, int groupId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ 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) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
@@ -175,7 +178,7 @@
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- bundle, dialogReceiver, mStatusBarService);
+ bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -344,6 +347,45 @@
// TODO: confirm security token when we move timeout management into the HAL layer.
mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
}
+
+ @Override
+ public int setRequireAttention(boolean requireAttention, final byte[] token) {
+ checkPermission(MANAGE_BIOMETRIC);
+
+ final ArrayList<Byte> byteToken = new ArrayList<>();
+ for (int i = 0; i < token.length; i++) {
+ byteToken.add(token[i]);
+ }
+
+ int result;
+ try {
+ result = mDaemon != null ? mDaemon.setRequireAttention(requireAttention, byteToken)
+ : Status.INTERNAL_ERROR;
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to setRequireAttention to " + requireAttention);
+ result = Status.INTERNAL_ERROR;
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean getRequireAttention(final byte[] token) {
+ checkPermission(MANAGE_BIOMETRIC);
+
+ final ArrayList<Byte> byteToken = new ArrayList<>();
+ for (int i = 0; i < token.length; i++) {
+ byteToken.add(token[i]);
+ }
+
+ boolean result = true;
+ try {
+ result = mDaemon != null ? mDaemon.getRequireAttention(byteToken).value : true;
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to getRequireAttention");
+ }
+ return result;
+ }
}
/**
@@ -352,10 +394,10 @@
*/
private class BiometricPromptServiceListenerImpl implements ServiceListener {
- private IBiometricPromptServiceReceiver mBiometricPromptServiceReceiver;
+ private IBiometricServiceReceiver mBiometricServiceReceiver;
- public BiometricPromptServiceListenerImpl(IBiometricPromptServiceReceiver receiver) {
- mBiometricPromptServiceReceiver = receiver;
+ public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
+ mBiometricServiceReceiver = receiver;
}
@Override
@@ -364,8 +406,8 @@
/**
* Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
*/
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onAcquired(deviceId,
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onAcquired(deviceId,
FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
}
@@ -374,22 +416,22 @@
@Override
public void onAuthenticationSucceeded(long deviceId,
BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onAuthenticationSucceeded(deviceId);
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
}
}
@Override
public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onAuthenticationFailed(deviceId);
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
}
}
@Override
public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onError(deviceId, error,
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onError(deviceId, error,
FaceManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -729,8 +771,9 @@
}
@Override
- protected int getAppOp() {
- return AppOpsManager.OP_USE_FACE;
+ protected boolean checkAppOps(int uid, String opPackageName) {
+ return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
+ == AppOpsManager.MODE_ALLOWED;
}
@Override
@@ -776,30 +819,30 @@
return mDaemon;
}
- private long startPreEnroll(IBinder token) {
+ private long startGenerateChallenge(IBinder token) {
IBiometricsFace daemon = getFaceDaemon();
if (daemon == null) {
- Slog.w(TAG, "startPreEnroll: no face HAL!");
+ Slog.w(TAG, "startGenerateChallenge: no face HAL!");
return 0;
}
try {
- return daemon.generateChallenge().value;
+ return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
} catch (RemoteException e) {
- Slog.e(TAG, "startPreEnroll failed", e);
+ Slog.e(TAG, "startGenerateChallenge failed", e);
}
return 0;
}
- private int startPostEnroll(IBinder token) {
+ private int startRevokeChallenge(IBinder token) {
IBiometricsFace daemon = getFaceDaemon();
if (daemon == null) {
- Slog.w(TAG, "startPostEnroll: no face HAL!");
+ Slog.w(TAG, "startRevokeChallenge: no face HAL!");
return 0;
}
try {
return daemon.revokeChallenge();
} catch (RemoteException e) {
- Slog.e(TAG, "startPostEnroll failed", e);
+ Slog.e(TAG, "startRevokeChallenge failed", e);
}
return 0;
}
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 9f4fff8..b0b788f 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -31,7 +31,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricPromptServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
@@ -59,7 +59,7 @@
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
-import com.android.server.biometrics.BiometricService;
+import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.ClientMonitor;
import com.android.server.biometrics.EnumerateClient;
@@ -84,7 +84,7 @@
*
* @hide
*/
-public class FingerprintService extends BiometricService {
+public class FingerprintService extends BiometricServiceBase {
protected static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
@@ -110,10 +110,11 @@
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted,
- owner, bundle, dialogReceiver, statusBarService);
+ restricted, owner, bundle, dialogReceiver, statusBarService,
+ requireConfirmation);
}
@Override
@@ -182,14 +183,13 @@
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
- null /* dialogReceiver */, mStatusBarService);
-
+ null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
public void authenticateFromService(IBinder token, long opId, int groupId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ IBiometricServiceReceiver receiver, int flags, String opPackageName,
Bundle bundle, IBiometricPromptReceiver dialogReceiver,
int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
@@ -198,7 +198,7 @@
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService);
+ dialogReceiver, mStatusBarService, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -390,17 +390,17 @@
*/
private class BiometricPromptServiceListenerImpl implements ServiceListener {
- private IBiometricPromptServiceReceiver mBiometricPromptServiceReceiver;
+ private IBiometricServiceReceiver mBiometricServiceReceiver;
- public BiometricPromptServiceListenerImpl(IBiometricPromptServiceReceiver receiver) {
- mBiometricPromptServiceReceiver = receiver;
+ public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
+ mBiometricServiceReceiver = receiver;
}
@Override
public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onAcquired(deviceId, acquiredInfo,
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onAcquired(deviceId, acquiredInfo,
FingerprintManager.getAcquiredString(
getContext(), acquiredInfo, vendorCode));
}
@@ -409,22 +409,22 @@
@Override
public void onAuthenticationSucceeded(long deviceId,
BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onAuthenticationSucceeded(deviceId);
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
}
}
@Override
public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onAuthenticationFailed(deviceId);
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
}
}
@Override
public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricPromptServiceReceiver != null) {
- mBiometricPromptServiceReceiver.onError(deviceId, error,
+ if (mBiometricServiceReceiver != null) {
+ mBiometricServiceReceiver.onError(deviceId, error,
FingerprintManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -514,7 +514,7 @@
/**
* An internal class to help clean up unknown fingerprints in the hardware and software.
*/
- private final class InternalEnumerateClient extends BiometricService.EnumerateClientImpl {
+ private final class InternalEnumerateClient extends BiometricServiceBase.EnumerateClientImpl {
private List<Fingerprint> mEnrolledList;
private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
@@ -577,7 +577,7 @@
/**
* An internal class to help clean up unknown fingerprints in hardware and software.
*/
- private final class InternalRemovalClient extends BiometricService.RemovalClientImpl {
+ private final class InternalRemovalClient extends BiometricServiceBase.RemovalClientImpl {
public InternalRemovalClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
@@ -902,8 +902,16 @@
}
@Override
- protected int getAppOp() {
- return AppOpsManager.OP_USE_FINGERPRINT;
+ protected boolean checkAppOps(int uid, String opPackageName) {
+ boolean appOpsOk = false;
+ if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
+ == AppOpsManager.MODE_ALLOWED) {
+ appOpsOk = true;
+ } else if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
+ == AppOpsManager.MODE_ALLOWED) {
+ appOpsOk = true;
+ }
+ return appOpsOk;
}
@Override
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index c028a43..a769590 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -629,6 +629,11 @@
if (mAppOps.noteOp(op, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
return false;
}
+ // Shell can access the clipboard for testing purposes.
+ if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
+ callingPackage) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
// The default IME is always allowed to access the clipboard.
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, UserHandle.getUserId(callingUid));
@@ -639,12 +644,20 @@
}
}
- // Otherwise only focused applications can access the clipboard.
- boolean uidFocused = mWm.isUidFocused(callingUid);
- if (!uidFocused) {
- Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus.");
+ switch (op) {
+ case AppOpsManager.OP_READ_CLIPBOARD:
+ // Clipboard can only be read by applications with focus.
+ boolean uidFocused = mWm.isUidFocused(callingUid);
+ if (!uidFocused) {
+ Slog.e(TAG, "Denying clipboard access to " + callingPackage
+ + ", application is not in focus.");
+ }
+ return uidFocused;
+ case AppOpsManager.OP_WRITE_CLIPBOARD:
+ // Writing is allowed without focus.
+ return true;
+ default:
+ throw new IllegalArgumentException("Unknown clipboard appop " + op);
}
- return uidFocused;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2a80f0e..48082b6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -163,8 +163,8 @@
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
- private Context mContext;
- private NetworkInfo mNetworkInfo;
+ private final Context mContext;
+ private final NetworkInfo mNetworkInfo;
private String mPackage;
private int mOwnerUID;
private String mInterface;
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 2b1d919..1e6bb04 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -320,9 +320,8 @@
}
private static boolean getEnableLegacyDhcpServer(Context ctx) {
- // TODO: make the default false (0) and update javadoc in Settings.java
final ContentResolver cr = ctx.getContentResolver();
- final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+ final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
return intVal != 0;
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index d8d650b..5698fdf 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1226,7 +1226,7 @@
if (userId == UserHandle.USER_ALL) {
mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, "No access to " + uri);
} else if (userId < 0) {
throw new IllegalArgumentException("Invalid user: " + userId);
} else if (userId != UserHandle.getCallingUserId()) {
@@ -1247,7 +1247,7 @@
? (Manifest.permission.INTERACT_ACROSS_USERS_FULL + " or " +
Manifest.permission.INTERACT_ACROSS_USERS)
: Manifest.permission.INTERACT_ACROSS_USERS_FULL;
- throw new SecurityException(TAG + "Neither user " + uid
+ throw new SecurityException("No access to " + uri + ": neither user " + uid
+ " nor current process has " + permissions);
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
index d4932f9..2fdcb51 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -25,6 +25,9 @@
// State that waits for <Active Source> once send <Request Active Source>.
private static final int STATE_WAITING_FOR_ACTIVE_SOURCE = 1;
+ // State that waits for TV supporting Audio System Mode or not
+ // once received <Active Source>
+ private static final int STATE_WAITING_FOR_TV_SUPPORT = 2;
@VisibleForTesting
static final int MAX_RETRY_COUNT = 5;
@@ -60,6 +63,7 @@
if (physicalAddress != getSourcePath()) {
audioSystem().setActiveSource(cmd.getSource(), physicalAddress);
}
+ mState = STATE_WAITING_FOR_TV_SUPPORT;
queryTvSystemAudioModeSupport();
return true;
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index a050aa9..0f284391 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1875,30 +1875,36 @@
// Read partner-provided list of excluded input devices
XmlPullParser parser = null;
// Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
- File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
- FileReader confreader = null;
- try {
- confreader = new FileReader(confFile);
- parser = Xml.newPullParser();
- parser.setInput(confreader);
- XmlUtils.beginDocument(parser, "devices");
+ final File[] baseDirs = {
+ Environment.getRootDirectory(),
+ Environment.getVendorDirectory()
+ };
+ 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;
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ names.add(name);
+ }
}
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- names.add(name);
- }
+ } 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) { }
}
- } 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) { }
}
return names.toArray(new String[names.size()]);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b5a9f74..3264790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,45 +18,11 @@
import android.content.ComponentName;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-
/**
* Input method manager local system service interface.
*/
public abstract class InputMethodManagerInternal {
/**
- * Called by the window manager service when a client process is being attached to the window
- * manager service.
- *
- * <p>The caller must not have WindowManagerService lock. This method internally acquires
- * InputMethodManagerService lock.</p>
- *
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- * @param inputContext communication channel for the dummy
- * {@link android.view.inputmethod.InputConnection}
- * @param uid UID of the client process
- * @param pid PID of the client process
- */
- public abstract void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
- int pid);
-
- /**
- * Called by the window manager service when a client process is being attached to the window
- * manager service.
- *
- * <p>The caller must not have WindowManagerService lock. This method internally acquires
- * InputMethodManagerService lock.</p>
- *
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- */
- public abstract void removeClient(IInputMethodClient client);
-
- /**
* Called by the power manager to tell the input method manager whether it
* should start watching for wake events.
*/
@@ -78,15 +44,6 @@
public static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext,
- int uid, int pid) {
- }
-
- @Override
- public void removeClient(IInputMethodClient client) {
- }
-
- @Override
public void setInteractive(boolean interactive) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b2287ac..a9b0d5c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -401,12 +401,28 @@
}
}
+ private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
+ private final InputMethodManagerService mImms;
+ private final IInputMethodClient mClient;
+
+ ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
+ mImms = imms;
+ mClient = client;
+ }
+
+ @Override
+ public void binderDied() {
+ mImms.removeClient(mClient);
+ }
+ }
+
static final class ClientState {
final IInputMethodClient client;
final IInputContext inputContext;
final int uid;
final int pid;
final InputBinding binding;
+ final ClientDeathRecipient clientDeathRecipient;
boolean sessionRequested;
SessionState curSession;
@@ -419,12 +435,13 @@
}
ClientState(IInputMethodClient _client, IInputContext _inputContext,
- int _uid, int _pid) {
+ int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) {
client = _client;
inputContext = _inputContext;
uid = _uid;
pid = _pid;
binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
+ clientDeathRecipient = _clientDeathRecipient;
}
}
@@ -1716,9 +1733,39 @@
}
}
- void addClient(ClientState clientState) {
+ /**
+ * Called by each application process as a preparation to start interacting with
+ * {@link InputMethodManagerService}.
+ *
+ * <p>As a general principle, IPCs from the application process that take
+ * {@link InputMethodClient} will be rejected without this step.</p>
+ *
+ * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
+ * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
+ * process
+ * @param inputContext communication channel for the dummy
+ * {@link android.view.inputmethod.InputConnection}
+ */
+ @Override
+ public void addClient(IInputMethodClient client, IInputContext inputContext) {
+ final int callerUid = Binder.getCallingUid();
+ final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
- mClients.put(clientState.client.asBinder(), clientState);
+ // TODO: Optimize this linear search.
+ for (ClientState state : mClients.values()) {
+ if (state.uid == callerUid && state.pid == callerPid) {
+ throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+ + " is already registered");
+ }
+ }
+ final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
+ try {
+ client.asBinder().linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ mClients.put(client.asBinder(),
+ new ClientState(client, inputContext, callerUid, callerPid, deathRecipient));
}
}
@@ -1726,6 +1773,7 @@
synchronized (mMethodMap) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
+ client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
clearClientSessionLocked(cs);
if (mCurClient == cs) {
if (mBoundToMethod) {
@@ -1928,7 +1976,7 @@
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
- unbindCurrentMethodLocked(true);
+ unbindCurrentMethodLocked();
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
@@ -1972,7 +2020,7 @@
mCurMethod = IInputMethod.Stub.asInterface(service);
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
@@ -2011,7 +2059,7 @@
channel.dispose();
}
- void unbindCurrentMethodLocked(boolean savePosition) {
+ void unbindCurrentMethodLocked() {
if (mVisibleBound) {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
@@ -2028,10 +2076,6 @@
Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ mCurTokenDisplayId);
}
- if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
- // The current IME is shown. Hence an IME switch (transition) is happening.
- mWindowManagerInternal.saveLastInputMethodWindowForTransition();
- }
mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
}
@@ -2046,7 +2090,7 @@
void resetCurrentMethodAndClient(
/* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
mCurMethodId = null;
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
unbindCurrentClientLocked(unbindClientReason);
}
@@ -2536,7 +2580,11 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+ final ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
return false;
}
@@ -2616,7 +2664,11 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+ final ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
if (DEBUG) {
Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
}
@@ -2734,7 +2786,7 @@
+ client.asBinder());
}
- if (!mWindowManagerInternal.inputMethodClientHasFocus(cs.client)) {
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
// Check with the window manager to make sure this client actually
// has a window with focus. If not, reject. This is thread safe
// because if the focus changes some time before or after, the
@@ -2809,7 +2861,7 @@
final int newFocusDisplayId =
mWindowManagerInternal.getDisplayIdForWindow(windowToken);
if (newFocusDisplayId != mCurTokenDisplayId) {
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
}
}
} else if (isTextEditor && doAutoShow && (softInputMode &
@@ -3181,19 +3233,6 @@
}
@BinderThread
- private void clearLastInputMethodWindowForTransition(IBinder token) {
- if (!calledFromValidUser()) {
- return;
- }
- synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
- return;
- }
- }
- mWindowManagerInternal.clearLastInputMethodWindowForTransition();
- }
-
- @BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
Slog.d(TAG, "Got the notification of a user action.");
@@ -4399,20 +4438,6 @@
}
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
- int pid) {
- // Work around Bug 113877122: We need to handle this synchronously. Otherwise, some
- // IMM binder calls from the client process before we register this client.
- mService.addClient(new ClientState(client, inputContext, uid, pid));
- }
-
- @Override
- public void removeClient(IInputMethodClient client) {
- // Handle this synchronously to be consistent with addClient().
- mService.removeClient(client);
- }
-
- @Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
@@ -4915,7 +4940,7 @@
try {
synchronized (mMethodMap) {
hideCurrentInputLocked(0, null);
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
// Reset the current IME
resetSelectedInputMethodAndSubtypeLocked(null);
// Also reset the settings of the current IME
@@ -4988,12 +5013,6 @@
@BinderThread
@Override
- public void clearLastInputMethodWindowForTransition() {
- mImms.clearLastInputMethodWindowForTransition(mToken);
- }
-
- @BinderThread
- @Override
public IInputContentUriToken createInputContentUriToken(Uri contentUri,
String packageName) {
return mImms.createInputContentUriToken(mToken, contentUri, packageName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e53eeb0..5005ea7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1152,15 +1152,18 @@
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
- final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
- // reload per-user settings
- mSettingsObserver.update(null);
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
- // Refresh managed services
- mConditionProviders.onUserSwitched(user);
- mListeners.onUserSwitched(user);
- mAssistants.onUserSwitched(user);
- mZenModeHelper.onUserSwitched(user);
+ if (!mUserProfiles.isManagedProfile(userId)) {
+ // reload per-user settings
+ mSettingsObserver.update(null);
+ // Refresh managed services
+ mConditionProviders.onUserSwitched(userId);
+ mListeners.onUserSwitched(userId);
+ mZenModeHelper.onUserSwitched(userId);
+ }
+ // assistant is the only thing that cares about managed profiles specifically
+ mAssistants.onUserSwitched(userId);
} else if (action.equals(Intent.ACTION_USER_ADDED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
if (userId != USER_NULL) {
@@ -1170,20 +1173,23 @@
}
}
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
- final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
- mZenModeHelper.onUserRemoved(user);
- mPreferencesHelper.onUserRemoved(user);
- mListeners.onUserRemoved(user);
- mConditionProviders.onUserRemoved(user);
- mAssistants.onUserRemoved(user);
+ mZenModeHelper.onUserRemoved(userId);
+ mPreferencesHelper.onUserRemoved(userId);
+ mListeners.onUserRemoved(userId);
+ mConditionProviders.onUserRemoved(userId);
+ mAssistants.onUserRemoved(userId);
savePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
- final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
- mConditionProviders.onUserUnlocked(user);
- mListeners.onUserUnlocked(user);
- mAssistants.onUserUnlocked(user);
- mZenModeHelper.onUserUnlocked(user);
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ mUserProfiles.updateCache(context);
+ mAssistants.onUserUnlocked(userId);
+ if (!mUserProfiles.isManagedProfile(userId)) {
+ mConditionProviders.onUserUnlocked(userId);
+ mListeners.onUserUnlocked(userId);
+ mZenModeHelper.onUserUnlocked(userId);
+ }
}
}
};
@@ -2391,7 +2397,7 @@
String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, Binder.getCallingUid(), false, false);
+ pkg, Binder.getCallingUid(), false, false, true);
}
@Override
@@ -2467,7 +2473,8 @@
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
String pkg, int uid, boolean includeDeleted) {
checkCallerIsSystem();
- return mPreferencesHelper.getNotificationChannelGroups(pkg, uid, includeDeleted, true);
+ return mPreferencesHelper.getNotificationChannelGroups(
+ pkg, uid, includeDeleted, true, false);
}
@Override
@@ -4277,8 +4284,13 @@
@VisibleForTesting
int resolveNotificationUid(String callingPkg, String targetPkg,
int callingUid, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ userId = USER_SYSTEM;
+ }
// posted from app A on behalf of app A
- if (isCallerSameApp(targetPkg, callingUid) && TextUtils.equals(callingPkg, targetPkg)) {
+ if (isCallerSameApp(targetPkg, callingUid, userId)
+ && (TextUtils.equals(callingPkg, targetPkg)
+ || isCallerSameApp(callingPkg, callingUid, userId))) {
return callingUid;
}
@@ -4295,7 +4307,8 @@
return targetUid;
}
- throw new SecurityException("Caller " + callingUid + " cannot post for pkg " + targetPkg);
+ throw new SecurityException("Caller " + callingPkg + ":" + callingUid
+ + " cannot post for pkg " + targetPkg + " in user " + userId);
}
/**
@@ -4315,7 +4328,7 @@
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, callingUid)) {
+ && isCallerInstantApp(pkg, Binder.getCallingUid(), 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
@@ -4721,7 +4734,8 @@
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
mListeners.notifyPostedLocked(r, old);
- if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
+ if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
+ && !isCritical(r)) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -4891,6 +4905,19 @@
}
/**
+ * Check if the notification is classified as critical.
+ *
+ * @param record the record to test for criticality
+ * @return {@code true} if notification is considered critical
+ *
+ * @see CriticalNotificationExtractor for criteria
+ */
+ private boolean isCritical(NotificationRecord record) {
+ // 0 is the most critical
+ return record.getCriticality() < CriticalNotificationExtractor.NORMAL;
+ }
+
+ /**
* Keeps the last 5 packages that have notified, by user.
*/
@GuardedBy("mNotificationLock")
@@ -6409,7 +6436,8 @@
}
}
- private boolean isCallerInstantApp(String pkg, int callingUid) {
+ @VisibleForTesting
+ boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
// System is always allowed to act for ephemeral apps.
if (isUidSystemOrPhone(callingUid)) {
return false;
@@ -6418,8 +6446,7 @@
mAppOps.checkPackage(callingUid, pkg);
try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
- UserHandle.getCallingUserId());
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
@@ -6431,13 +6458,13 @@
}
private void checkCallerIsSameApp(String pkg) {
- checkCallerIsSameApp(pkg, Binder.getCallingUid());
+ checkCallerIsSameApp(pkg, Binder.getCallingUid(), UserHandle.getCallingUserId());
}
- private void checkCallerIsSameApp(String pkg, int uid) {
+ private void checkCallerIsSameApp(String pkg, int uid, int userId) {
try {
ApplicationInfo ai = mPackageManager.getApplicationInfo(
- pkg, 0, UserHandle.getCallingUserId());
+ pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
@@ -6459,9 +6486,9 @@
}
}
- private boolean isCallerSameApp(String pkg, int uid) {
+ private boolean isCallerSameApp(String pkg, int uid, int userId) {
try {
- checkCallerIsSameApp(pkg, uid);
+ checkCallerIsSameApp(pkg, uid, userId);
return true;
} catch (SecurityException e) {
return false;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 13ff6e8..3a0ab77 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -755,7 +755,7 @@
@Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped) {
+ int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Preconditions.checkNotNull(pkg);
Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
PackagePreferences r = getPackagePreferences(pkg, uid);
@@ -786,6 +786,13 @@
if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
groups.put(null, nonGrouped);
}
+ if (includeEmpty) {
+ for (NotificationChannelGroup group : r.groups.values()) {
+ if (!groups.containsKey(group.getId())) {
+ groups.put(group.getId(), group);
+ }
+ }
+ }
return new ParceledListSlice<>(new ArrayList<>(groups.values()));
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index af64683..605348b 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -36,7 +36,7 @@
void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp);
ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped);
+ int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess);
void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4dd2bf2..760f155 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -52,8 +52,6 @@
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.EventInfo;
-import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeProto;
import android.util.AndroidRuntimeException;
@@ -119,8 +117,6 @@
public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
| SUPPRESSED_EFFECT_NOTIFICATIONS;
- protected String mDefaultRuleEveryNightName;
- protected String mDefaultRuleEventsName;
@VisibleForTesting protected boolean mIsBootComplete;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
@@ -130,9 +126,9 @@
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mNotificationManager = context.getSystemService(NotificationManager.class);
- mDefaultConfig = new ZenModeConfig();
- setDefaultZenRules(mContext);
- mConfig = mDefaultConfig;
+ mDefaultConfig = readDefaultConfig(mContext.getResources());
+ updateDefaultAutomaticRuleNames();
+ mConfig = mDefaultConfig.copy();
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
mSettingsObserver = new SettingsObserver(mHandler);
@@ -311,7 +307,9 @@
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
- newConfig.automaticRules.put(rule.id, rule);
+ if (newConfig.automaticRules.put(rule.id, rule) != null) {
+ rule.modified = true;
+ }
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
@@ -341,7 +339,9 @@
}
}
populateZenRule(automaticZenRule, rule, false);
- newConfig.automaticRules.put(ruleId, rule);
+ if (newConfig.automaticRules.put(ruleId, rule) != null) {
+ rule.modified = true;
+ }
return setConfigLocked(newConfig, reason, rule.component, true);
}
}
@@ -413,17 +413,6 @@
}
}
- public void setDefaultZenRules(Context context) {
- mDefaultConfig = readDefaultConfig(context.getResources());
- appendDefaultRules(mDefaultConfig);
- }
-
- private void appendDefaultRules (ZenModeConfig config) {
- getDefaultRuleNames();
- appendDefaultEveryNightRule(config);
- appendDefaultEventRules(config);
- }
-
// Checks zen rule properties are the same (doesn't check creation time, name nor enabled)
// used to check if default rules were customized or not
private boolean ruleValuesEqual(AutomaticZenRule rule, ZenRule defaultRule) {
@@ -437,22 +426,16 @@
}
protected void updateDefaultZenRules() {
- ZenModeConfig configDefaultRules = new ZenModeConfig();
- appendDefaultRules(configDefaultRules); // "new" localized default rules
- for (String ruleId : ZenModeConfig.DEFAULT_RULE_IDS) {
- AutomaticZenRule currRule = getAutomaticZenRule(ruleId);
- ZenRule defaultRule = configDefaultRules.automaticRules.get(ruleId);
- // if default rule wasn't customized, use localized name instead of previous
- if (ruleValuesEqual(currRule, defaultRule) &&
- !defaultRule.name.equals(currRule.getName())) {
+ updateDefaultAutomaticRuleNames();
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't modified, use localized name instead of previous
+ if (currRule != null && !currRule.modified && !defaultRule.name.equals(currRule.name)) {
if (canManageAutomaticZenRule(defaultRule)) {
if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.getName() + " to " + defaultRule.name);
+ + "from " + currRule.name + " to " + defaultRule.name);
// update default rule (if locale changed, name of rule will change)
- AutomaticZenRule defaultAutoRule = createAutomaticZenRule(defaultRule);
- // ensure enabled state is carried over from current rule
- defaultAutoRule.setEnabled(currRule.isEnabled());
- updateAutomaticZenRule(ruleId, defaultAutoRule,
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(defaultRule),
"locale changed");
}
}
@@ -642,7 +625,9 @@
// - doesn't already have default rules and
// - all previous automatic rules were disabled
config.automaticRules = new ArrayMap<>();
- appendDefaultRules(config);
+ for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ config.automaticRules.put(rule.id, rule);
+ }
reason += ", reset to default rules";
}
@@ -854,12 +839,16 @@
}
}
- private void getDefaultRuleNames() {
- // on locale-change, these values differ
- mDefaultRuleEveryNightName = mContext.getResources()
- .getString(R.string.zen_mode_default_every_night_name);
- mDefaultRuleEventsName = mContext.getResources()
- .getString(R.string.zen_mode_default_events_name);
+ private void updateDefaultAutomaticRuleNames() {
+ for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
+ rule.name = mContext.getResources()
+ .getString(R.string.zen_mode_default_events_name);
+ } else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) {
+ rule.name = mContext.getResources()
+ .getString(R.string.zen_mode_default_every_night_name);
+ }
+ }
}
@VisibleForTesting
@@ -1001,42 +990,6 @@
return new ZenModeConfig();
}
- private void appendDefaultEveryNightRule(ZenModeConfig config) {
- if (config == null) return;
-
- final ScheduleInfo weeknights = new ScheduleInfo();
- weeknights.days = ZenModeConfig.ALL_DAYS;
- weeknights.startHour = 22;
- weeknights.endHour = 7;
- weeknights.exitAtAlarm = true;
- final ZenRule rule = new ZenRule();
- rule.enabled = false;
- rule.name = mDefaultRuleEveryNightName;
- rule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
- rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- rule.component = ScheduleConditionProvider.COMPONENT;
- rule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
- rule.creationTime = System.currentTimeMillis();
- config.automaticRules.put(rule.id, rule);
- }
-
- private void appendDefaultEventRules(ZenModeConfig config) {
- if (config == null) return;
-
- final EventInfo events = new EventInfo();
- events.calendar = null; // any calendar
- events.reply = EventInfo.REPLY_YES_OR_MAYBE;
- final ZenRule rule = new ZenRule();
- rule.enabled = false;
- rule.name = mDefaultRuleEventsName;
- rule.conditionId = ZenModeConfig.toEventConditionId(events);
- rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- rule.component = EventConditionProvider.COMPONENT;
- rule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
- rule.creationTime = System.currentTimeMillis();
- config.automaticRules.put(rule.id, rule);
- }
-
private static int zenSeverity(int zen) {
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1;
@@ -1208,13 +1161,13 @@
final boolean showNotification = mIsBootComplete
&& zen != Global.ZEN_MODE_OFF
&& !isWatch
- && Settings.Global.getInt(mContext.getContentResolver(),
+ && Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0
- && Settings.Global.getInt(mContext.getContentResolver(),
+ && Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ZEN_SETTINGS_UPDATED, 0) != 1;
if (isWatch) {
- Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 5bf323a..3b11525 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1049,7 +1049,7 @@
(other != null && other.getComponentName() != null)
? other.getComponentName().getPackageName() : "?";
// if we're installing over the same already-installed package, this is ok
- if (otherPackageName != pkg.packageName) {
+ if (!otherPackageName.equals(pkg.packageName)) {
throw new PackageManagerException(
INSTALL_FAILED_CONFLICTING_PROVIDER,
"Can't install because provider name " + names[j]
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8f2833f..006ea75 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -21,6 +21,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
@@ -1060,6 +1061,7 @@
@GuardedBy("mLock")
private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
throws PackageManagerException {
+ ApkLite baseApk = null;
mPackageName = null;
mVersionCode = -1;
mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
@@ -1136,6 +1138,7 @@
// Base is coming from session
if (apk.splitName == null) {
mResolvedBaseFile = targetFile;
+ baseApk = apk;
}
mResolvedStagedFiles.add(targetFile);
@@ -1221,6 +1224,7 @@
if (baseDexMetadataFile != null) {
mResolvedInheritedFiles.add(baseDexMetadataFile);
}
+ baseApk = existingBase;
}
// Inherit splits if not overridden
@@ -1300,6 +1304,10 @@
}
}
}
+ if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
+ "Missing split for " + mPackageName);
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 91af0ec..329b1da 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -232,6 +232,7 @@
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.permission.PermissionManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
@@ -2912,13 +2913,15 @@
if (mIsUpgrade) {
final int callingUid = getCallingUid();
- final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+ final List<PermissionManager.SplitPermissionInfo> splitPermissions =
+ mContext.getSystemService(PermissionManager.class).getSplitPermissions();
+ final int numSplitPerms = splitPermissions.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- final PackageParser.SplitPermissionInfo splitPerm =
- PackageParser.SPLIT_PERMISSIONS[splitPermNum];
- final String rootPerm = splitPerm.rootPerm;
+ final PermissionManager.SplitPermissionInfo splitPerm =
+ splitPermissions.get(splitPermNum);
+ final String rootPerm = splitPerm.getRootPermission();
- if (preUpgradeSdkVersion >= splitPerm.targetSdk) {
+ if (preUpgradeSdkVersion >= splitPerm.getTargetSdk()) {
continue;
}
@@ -2926,7 +2929,7 @@
for (int packageNum = 0; packageNum < numPackages; packageNum++) {
final PackageParser.Package pkg = mPackages.valueAt(packageNum);
- if (pkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk
+ if (pkg.applicationInfo.targetSdkVersion >= splitPerm.getTargetSdk()
|| !pkg.requestedPermissions.contains(rootPerm)) {
continue;
}
@@ -2938,7 +2941,7 @@
continue;
}
- final String[] newPerms = splitPerm.newPerms;
+ final String[] newPerms = splitPerm.getNewPermissions();
final int numNewPerms = newPerms.length;
for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
@@ -8471,7 +8474,7 @@
private boolean canSkipFullApkVerification(String apkPath) {
final byte[] rootHashObserved;
try {
- rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
+ rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
if (rootHashObserved == null) {
return false; // APK does not contain Merkle tree root hash.
}
@@ -15336,7 +15339,9 @@
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
mArtManagerService.prepareAppProfiles(
- pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier()));
+ pkg,
+ resolveUserIds(reconciledPkg.installForUser.getIdentifier()),
+ /* updateReferenceProfileContent= */ true);
// Check whether we need to dexopt the app.
//
@@ -15999,12 +16004,14 @@
}
if (apkPath != null) {
final VerityUtils.SetupResult result =
- VerityUtils.generateApkVeritySetupData(apkPath);
+ VerityUtils.generateApkVeritySetupData(apkPath, null /* signaturePath */,
+ true /* skipSigningBlock */);
if (result.isOk()) {
if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
FileDescriptor fd = result.getUnownedFileDescriptor();
try {
- final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
+ final byte[] signedRootHash =
+ VerityUtils.generateApkVerityRootHash(apkPath);
mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
} catch (InstallerException | IOException | DigestException |
@@ -21184,8 +21191,18 @@
//
// We also have to cover non system users because we do not call the usual install package
// methods for them.
+ //
+ // NOTE: in order to speed up first boot time we only create the current profile and do not
+ // update the content of the reference profile. A system image should already be configured
+ // with the right profile keys and the profiles for the speed-profile prebuilds should
+ // already be copied. That's done in #performDexOptUpgrade.
+ //
+ // TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of
+ // manually copying them in #performDexOptUpgrade. When we do that we should have a more
+ // granular check here and only update the existing profiles.
if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
- mArtManagerService.prepareAppProfiles(pkg, userId);
+ mArtManagerService.prepareAppProfiles(pkg, userId,
+ /* updateReferenceProfileContent= */ false);
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
@@ -23082,7 +23099,9 @@
return false;
}
}
- if (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)) {
+ if (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)
+ || sUserManager.hasUserRestriction(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) {
return false;
}
if (mExternalSourcesPolicy != null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 361416a..93729d1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -124,6 +124,7 @@
int mTargetUser;
boolean mBrief;
boolean mComponents;
+ int mQueryFlags;
PackageManagerShellCommand(PackageManagerService service) {
mInterface = service;
@@ -739,6 +740,9 @@
} else if ("--components".equals(opt)) {
mComponents = true;
return true;
+ } else if ("--query-flags".equals(opt)) {
+ mQueryFlags = Integer.decode(cmd.getNextArgRequired());
+ return true;
}
return false;
}
@@ -784,7 +788,8 @@
throw new RuntimeException(e.getMessage(), e);
}
try {
- ResolveInfo ri = mInterface.resolveIntent(intent, intent.getType(), 0, mTargetUser);
+ ResolveInfo ri = mInterface.resolveIntent(intent, intent.getType(), mQueryFlags,
+ mTargetUser);
PrintWriter pw = getOutPrintWriter();
if (ri == null) {
pw.println("No activity found");
@@ -806,8 +811,8 @@
throw new RuntimeException(e.getMessage(), e);
}
try {
- List<ResolveInfo> result = mInterface.queryIntentActivities(intent, intent.getType(), 0,
- mTargetUser).getList();
+ List<ResolveInfo> result = mInterface.queryIntentActivities(intent, intent.getType(),
+ mQueryFlags, mTargetUser).getList();
PrintWriter pw = getOutPrintWriter();
if (result == null || result.size() <= 0) {
pw.println("No activities found");
@@ -840,8 +845,8 @@
throw new RuntimeException(e.getMessage(), e);
}
try {
- List<ResolveInfo> result = mInterface.queryIntentServices(intent, intent.getType(), 0,
- mTargetUser).getList();
+ List<ResolveInfo> result = mInterface.queryIntentServices(intent, intent.getType(),
+ mQueryFlags, mTargetUser).getList();
PrintWriter pw = getOutPrintWriter();
if (result == null || result.size() <= 0) {
pw.println("No services found");
@@ -874,8 +879,8 @@
throw new RuntimeException(e.getMessage(), e);
}
try {
- List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, intent.getType(), 0,
- mTargetUser).getList();
+ List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, intent.getType(),
+ mQueryFlags, mTargetUser).getList();
PrintWriter pw = getOutPrintWriter();
if (result == null || result.size() <= 0) {
pw.println("No receivers found");
@@ -2234,6 +2239,9 @@
case "--install-location":
sessionParams.installLocation = Integer.parseInt(getNextArg());
break;
+ case "--install-reason":
+ sessionParams.installReason = Integer.parseInt(getNextArg());
+ break;
case "--force-uuid":
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
sessionParams.volumeUuid = getNextArg();
@@ -2728,22 +2736,26 @@
pw.println(" -d: only list dangerous permissions");
pw.println(" -u: list only the permissions users will see");
pw.println("");
- pw.println(" resolve-activity [--brief] [--components] [--user USER_ID] INTENT");
+ pw.println(" resolve-activity [--brief] [--components] [--query-flags FLAGS]");
+ pw.println(" [--user USER_ID] INTENT");
pw.println(" Prints the activity that resolves to the given INTENT.");
pw.println("");
- pw.println(" query-activities [--brief] [--components] [--user USER_ID] INTENT");
+ pw.println(" query-activities [--brief] [--components] [--query-flags FLAGS]");
+ pw.println(" [--user USER_ID] INTENT");
pw.println(" Prints all activities that can handle the given INTENT.");
pw.println("");
- pw.println(" query-services [--brief] [--components] [--user USER_ID] INTENT");
+ pw.println(" query-services [--brief] [--components] [--query-flags FLAGS]");
+ pw.println(" [--user USER_ID] INTENT");
pw.println(" Prints all services that can handle the given INTENT.");
pw.println("");
- pw.println(" query-receivers [--brief] [--components] [--user USER_ID] INTENT");
+ pw.println(" query-receivers [--brief] [--components] [--query-flags FLAGS]");
+ pw.println(" [--user USER_ID] INTENT");
pw.println(" Prints all broadcast receivers that can handle the given INTENT.");
pw.println("");
pw.println(" install [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
- pw.println(" [--originating-uri URI] [---referrer URI]");
- pw.println(" [--abi ABI_NAME] [--force-sdk]");
+ pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
+ pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
pw.println(" Install an application. Must provide the apk data to install, either as a");
@@ -2768,14 +2780,17 @@
pw.println(" --full: cause the app to be installed as a non-ephemeral full app");
pw.println(" --install-location: force the install location:");
pw.println(" 0=auto, 1=internal only, 2=prefer external");
+ pw.println(" --install-reason: indicates why the app is being installed:");
+ pw.println(" 0=unknown, 1=admin policy, 2=device restore,");
+ pw.println(" 3=device setup, 4=user request");
pw.println(" --force-uuid: force install on to disk volume with given UUID");
pw.println(" --force-sdk: allow install even when existing app targets platform");
pw.println(" codename but new one targets a final API level");
pw.println("");
pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
- pw.println(" [--originating-uri URI] [---referrer URI]");
- pw.println(" [--abi ABI_NAME] [--force-sdk]");
+ pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
+ pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 3f28ee6..1315502 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -16,10 +16,6 @@
package com.android.server.pm;
-import com.google.android.collect.Sets;
-
-import com.android.internal.util.Preconditions;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -42,6 +38,10 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.Preconditions;
+
+import com.google.android.collect.Sets;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -77,6 +77,7 @@
UserManager.DISALLOW_UNINSTALL_APPS,
UserManager.DISALLOW_SHARE_LOCATION,
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
UserManager.DISALLOW_CONFIG_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
@@ -211,7 +212,8 @@
*/
private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
UserManager.ENSURE_VERIFY_APPS,
- UserManager.DISALLOW_AIRPLANE_MODE
+ UserManager.DISALLOW_AIRPLANE_MODE,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
);
/**
@@ -517,13 +519,18 @@
userId);
}
break;
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY:
+ setInstallMarketAppsRestriction(cr, userId, getNewUserRestrictionSetting(
+ context, userId, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ newValue));
+ break;
case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
// Since Android O, the secure setting is not available to be changed by the
// user. Hence, when the restriction is cleared, we need to reset the state of
// the setting to its default value which is now 1.
- android.provider.Settings.Secure.putIntForUser(cr,
- android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
- newValue ? 0 : 1, userId);
+ setInstallMarketAppsRestriction(cr, userId, getNewUserRestrictionSetting(
+ context, userId, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
+ newValue));
break;
case UserManager.DISALLOW_RUN_IN_BACKGROUND:
if (newValue) {
@@ -813,4 +820,16 @@
}
return false;
}
+
+ private static void setInstallMarketAppsRestriction(ContentResolver cr, int userId,
+ int settingValue) {
+ android.provider.Settings.Secure.putIntForUser(
+ cr, android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, settingValue, userId);
+ }
+
+ private static int getNewUserRestrictionSetting(Context context, int userId,
+ String userRestriction, boolean newValue) {
+ return (newValue || UserManager.get(context).hasUserRestriction(userRestriction,
+ UserHandle.of(userId))) ? 0 : 1;
+ }
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 21daa39..910ea73 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -389,7 +389,8 @@
* - create the current primary profile to save time at app startup time.
* - copy the profiles from the associated dex metadata file to the reference profile.
*/
- public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) {
+ public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,
+ boolean updateReferenceProfileContent) {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
if (user < 0) {
Slog.wtf(TAG, "Invalid user id: " + user);
@@ -404,8 +405,14 @@
for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
String codePath = codePathsProfileNames.keyAt(i);
String profileName = codePathsProfileNames.valueAt(i);
- File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
- String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+ String dexMetadataPath = null;
+ // Passing the dex metadata file to the prepare method will update the reference
+ // profile content. As such, we look for the dex metadata file only if we need to
+ // perform an update.
+ if (updateReferenceProfileContent) {
+ File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
+ dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+ }
synchronized (mInstaller) {
boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
profileName, codePath, dexMetadataPath);
@@ -423,9 +430,10 @@
/**
* Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
*/
- public void prepareAppProfiles(PackageParser.Package pkg, int[] user) {
+ public void prepareAppProfiles(PackageParser.Package pkg, int[] user,
+ boolean updateReferenceProfileContent) {
for (int i = 0; i < user.length; i++) {
- prepareAppProfiles(pkg, user[i]);
+ prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
}
}
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index e3e1590..602ce3b 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -93,7 +93,7 @@
@GuardedBy("mPackageUseInfoMap")
private final Map<String, PackageUseInfo> mPackageUseInfoMap;
- public PackageDexUsage() {
+ /* package */ PackageDexUsage() {
super("package-dex-usage.list", "PackageDexUsage_DiskWriter", /*lock*/ false);
mPackageUseInfoMap = new HashMap<>();
}
@@ -116,7 +116,7 @@
* @return true if the dex load constitutes new information, or false if this information
* has been seen before.
*/
- public boolean record(String owningPackageName, String dexPath, int ownerUserId,
+ /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId,
String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
String loadingPackageName, String classLoaderContext) {
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
@@ -193,7 +193,7 @@
* Convenience method for sync reads which does not force the user to pass a useless
* (Void) null.
*/
- public void read() {
+ /* package */ void read() {
read((Void) null);
}
@@ -558,7 +558,7 @@
* Remove the usage data associated with package {@code packageName}.
* @return true if the package usage was found and removed successfully.
*/
- public boolean removePackage(String packageName) {
+ /* package */ boolean removePackage(String packageName) {
synchronized (mPackageUseInfoMap) {
return mPackageUseInfoMap.remove(packageName) != null;
}
@@ -653,11 +653,12 @@
return packages;
}
- public void clear() {
+ /* package */ void clear() {
synchronized (mPackageUseInfoMap) {
mPackageUseInfoMap.clear();
}
}
+
// Creates a deep copy of the class' mPackageUseInfoMap.
private Map<String, PackageUseInfo> clonePackageUseInfoMap() {
Map<String, PackageUseInfo> clone = new HashMap<>();
@@ -679,7 +680,7 @@
throw new IllegalArgumentException("Unknown bool encoding: " + bool);
}
- public String dump() {
+ /* package */ String dump() {
StringWriter sw = new StringWriter();
write(sw);
return sw.toString();
diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
new file mode 100644
index 0000000..ad52559
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+ "presubmit": [
+ {
+ "name": "DexLoggerTests"
+ },
+ {
+ "name": "DexManagerTests"
+ },
+ {
+ "name": "DexoptOptionsTests"
+ },
+ {
+ "name": "DexoptUtilsTest"
+ },
+ {
+ "name": "PackageDexUsageTests"
+ },
+ {
+ "name": "DexLoggerIntegrationTests"
+ }
+ ]
+}
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 3c9dd63..6f644dd 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -34,7 +34,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackagesProvider;
import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
-import android.content.pm.PackageParser;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.media.RingtoneManager;
@@ -48,6 +47,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.print.PrintManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -1024,15 +1024,17 @@
ApplicationInfo applicationInfo = pkg.applicationInfo;
// Automatically attempt to grant split permissions to older APKs
- final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+ final List<PermissionManager.SplitPermissionInfo> splitPermissions =
+ mContext.getSystemService(PermissionManager.class).getSplitPermissions();
+ final int numSplitPerms = splitPermissions.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- final PackageParser.SplitPermissionInfo splitPerm =
- PackageParser.SPLIT_PERMISSIONS[splitPermNum];
+ final PermissionManager.SplitPermissionInfo splitPerm =
+ splitPermissions.get(splitPermNum);
if (applicationInfo != null
- && applicationInfo.targetSdkVersion < splitPerm.targetSdk
- && permissionsWithoutSplits.contains(splitPerm.rootPerm)) {
- Collections.addAll(permissions, splitPerm.newPerms);
+ && applicationInfo.targetSdkVersion < splitPerm.getTargetSdk()
+ && permissionsWithoutSplits.contains(splitPerm.getRootPermission())) {
+ Collections.addAll(permissions, splitPerm.getNewPermissions());
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index 5e66bfc3..82d6b22 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -135,7 +135,8 @@
final int userCount = other.mPermissionReviewRequired.size();
for (int i = 0; i < userCount; i++) {
final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
- mPermissionReviewRequired.put(i, reviewRequired);
+ mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i),
+ reviewRequired);
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3b19beb..2557f46 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -125,6 +125,7 @@
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
+
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -532,8 +533,6 @@
GlobalActions mGlobalActions;
Handler mHandler;
- WindowState mLastInputMethodWindow = null;
- WindowState mLastInputMethodTargetWindow = null;
// FIXME This state is shared between the input reader and handler thread.
// Technically it's broken and buggy but it has been like this for many years
@@ -631,9 +630,11 @@
InputConsumer mInputConsumer = null;
- private final WindowFrames mWindowFrames = new WindowFrames();
- private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
- private static final Rect mTmpRect = new Rect();
+ private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpDockedFrame = new Rect();
+ private static final Rect sTmpNavFrame = new Rect();
+ private static final Rect sTmpLastParentFrame = new Rect();
WindowState mTopFullscreenOpaqueWindowState;
WindowState mTopFullscreenOpaqueOrDimmingWindowState;
@@ -2827,7 +2828,8 @@
// Assumes it's safe to show starting windows of launched apps while
// the keyguard is being hidden. This is okay because starting windows never show
// secret information.
- if (mKeyguardOccluded) {
+ // TODO(b/113840485): Occluded may not only happen on default display
+ if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
}
}
@@ -4308,9 +4310,6 @@
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
- mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
-
if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
@@ -4377,12 +4376,8 @@
return;
}
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
- displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
- displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
- mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
- displayFrames.mDock /* outsetFrame */);
+ sTmpRect.setEmpty();
+ sTmpDockedFrame.set(displayFrames.mDock);
final int displayId = displayFrames.mDisplayId;
final Rect dockFrame = displayFrames.mDock;
@@ -4396,7 +4391,13 @@
continue;
}
- w.computeFrameLw(mWindowFrames);
+ w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
+ sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
+ sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
+ sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
+ sTmpDockedFrame /* outsetFrame */);
+ w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ w.computeFrameLw();
final Rect frame = w.getFrameLw();
if (frame.left <= 0 && frame.top <= 0) {
@@ -4448,17 +4449,17 @@
return false;
}
// apply any navigation bar insets
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ sTmpRect.setEmpty();
+ mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
displayFrames.mUnrestricted /* displayFrame */,
displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
- displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+ displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
-
+ mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
- mStatusBar.computeFrameLw(mWindowFrames);
+ mStatusBar.computeFrameLw();
// For layout, the status bar is always at the top with our fixed height.
displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4468,11 +4469,11 @@
displayFrames.mDisplayCutoutSafe.top);
// Tell the bar controller where the collapsed status bar content is
- mTmpRect.set(mStatusBar.getContentFrameLw());
- mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- mTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
- mTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
- mStatusBarController.setContentFrame(mTmpRect);
+ sTmpRect.set(mStatusBar.getContentFrameLw());
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
+ sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+ mStatusBarController.setContentFrame(sTmpRect);
boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
boolean statusBarTranslucent = (sysui
@@ -4512,7 +4513,7 @@
return false;
}
- final Rect navigationFrame = mWindowFrames.mParentFrame;
+ final Rect navigationFrame = sTmpNavFrame;
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4523,7 +4524,7 @@
final Rect dockFrame = displayFrames.mDock;
mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
- final Rect cutoutSafeUnrestricted = mTmpRect;
+ final Rect cutoutSafeUnrestricted = sTmpRect;
cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
@@ -4605,15 +4606,15 @@
displayFrames.mContent.set(dockFrame);
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+ sTmpRect.setEmpty();
+ mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
displayFrames.mDisplayCutoutSafe /* contentFrame */,
- navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+ navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
navigationFrame /* stableFrame */,
displayFrames.mDisplayCutoutSafe /* outsetFrame */);
-
- mNavigationBar.computeFrameLw(mWindowFrames);
+ mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ mNavigationBar.computeFrameLw();
mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
@@ -4730,12 +4731,6 @@
}
final WindowManager.LayoutParams attrs = win.getAttrs();
final boolean isDefaultDisplay = win.isDefaultDisplay();
- final boolean needsToOffsetInputMethodTarget =
- (win == mLastInputMethodTargetWindow) && (mLastInputMethodWindow != null);
- if (needsToOffsetInputMethodTarget) {
- if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
- offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
- }
final int type = attrs.type;
final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -4744,17 +4739,20 @@
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
- final Rect pf = mWindowFrames.mParentFrame;
- final Rect df = mWindowFrames.mDisplayFrame;
- final Rect of = mWindowFrames.mOverscanFrame;
- final Rect cf = mWindowFrames.mContentFrame;
- final Rect vf = mWindowFrames.mVisibleFrame;
- final Rect dcf = mWindowFrames.mDecorFrame;
- final Rect sf = mWindowFrames.mStableFrame;
+ final WindowFrames windowFrames = win.getWindowFrames();
+
+ windowFrames.setHasOutsets(false);
+ sTmpLastParentFrame.set(windowFrames.mParentFrame);
+ final Rect pf = windowFrames.mParentFrame;
+ final Rect df = windowFrames.mDisplayFrame;
+ final Rect of = windowFrames.mOverscanFrame;
+ final Rect cf = windowFrames.mContentFrame;
+ final Rect vf = windowFrames.mVisibleFrame;
+ final Rect dcf = windowFrames.mDecorFrame;
+ final Rect sf = windowFrames.mStableFrame;
dcf.setEmpty();
- mWindowFrames.mOutsetFrame.setEmpty();
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
- mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+ windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+ windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
@@ -4774,7 +4772,7 @@
cf.set(displayFrames.mDock);
of.set(displayFrames.mDock);
df.set(displayFrames.mDock);
- pf.set(displayFrames.mDock);
+ windowFrames.mParentFrame.set(displayFrames.mDock);
// IM dock windows layout below the nav bar...
pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
@@ -4876,9 +4874,7 @@
? displayFrames.mRestricted.bottom
: displayFrames.mUnrestricted.bottom;
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out status bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
// Asking to layout into the overscan region, so give it that pure
@@ -4950,17 +4946,13 @@
pf.bottom = df.bottom = of.bottom = cf.bottom =
displayFrames.mRestricted.bottom;
}
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
} else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
of.set(displayFrames.mUnrestricted);
df.set(displayFrames.mUnrestricted);
pf.set(displayFrames.mUnrestricted);
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out navigation bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
} else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
// Fullscreen secure system overlays get what they ask for. Screenshot region
@@ -5087,7 +5079,7 @@
// Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
// the cutout safe zone.
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
- final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
+ final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
&& cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
@@ -5122,9 +5114,9 @@
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
- mTmpRect.set(pf);
+ sTmpRect.set(pf);
pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf));
+ windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
}
// Make sure that NO_LIMITS windows clipped to the display don't extend under the
// cutout.
@@ -5152,8 +5144,9 @@
// apply the outsets to floating dialogs, because they wouldn't make sense there.
final boolean useOutsets = shouldUseOutsets(attrs, fl);
if (isDefaultDisplay && useOutsets) {
- final Rect osf = mWindowFrames.mOutsetFrame;
+ final Rect osf = windowFrames.mOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
+ windowFrames.setHasOutsets(true);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
int rotation = displayFrames.mRotation;
@@ -5180,14 +5173,17 @@
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ " dcf=" + dcf.toShortString()
+ " sf=" + sf.toShortString()
- + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
+ + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
- win.computeFrameLw(mWindowFrames);
+ if (!sTmpLastParentFrame.equals(pf)) {
+ windowFrames.setContentChanged(true);
+ }
+
+ win.computeFrameLw();
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
&& !win.getGivenInsetsPendingLw()) {
- setLastInputMethodWindowLw(null, null);
offsetInputMethodWindowLw(win, displayFrames);
}
if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
@@ -7818,12 +7814,6 @@
}
@Override
- public void setLastInputMethodWindowLw(WindowState ime, WindowState target) {
- mLastInputMethodWindow = ime;
- mLastInputMethodTargetWindow = target;
- }
-
- @Override
public void setDismissImeOnBackKeyPressed(boolean newValue) {
mDismissImeOnBackKeyPressed = newValue;
}
@@ -7841,7 +7831,6 @@
if (statusBar != null) {
statusBar.setCurrentUser(newUserId);
}
- setLastInputMethodWindowLw(null, null);
}
@Override
@@ -8016,14 +8005,6 @@
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
- if (mLastInputMethodWindow != null) {
- pw.print(prefix); pw.print("mLastInputMethodWindow=");
- pw.println(mLastInputMethodWindow);
- }
- if (mLastInputMethodTargetWindow != null) {
- pw.print(prefix); pw.print("mLastInputMethodTargetWindow=");
- pw.println(mLastInputMethodTargetWindow);
- }
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar=");
pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
new file mode 100644
index 0000000..e212b04
--- /dev/null
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -0,0 +1,50 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ }
+ ]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b55adeb..27ab3ef 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -63,12 +63,10 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -95,7 +93,6 @@
import com.android.server.wm.DisplayFrames;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
-import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -201,10 +198,8 @@
* getFrame() if so desired. Must be called with the window manager
* lock held.
*
- * @param windowFrames Container for all the window frames that affect how the window is
- * laid out.
*/
- public void computeFrameLw(WindowFrames windowFrames);
+ public void computeFrameLw();
/**
* Retrieve the current frame of the window that has been assigned by
@@ -477,6 +472,11 @@
* Writes {@link com.android.server.wm.IdentifierProto} to stream.
*/
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
+
+ /**
+ * @return The {@link WindowFrames} associated with this {@link WindowState}
+ */
+ WindowFrames getWindowFrames();
}
/**
@@ -1167,7 +1167,6 @@
default void layoutWindowLw(
WindowState win, WindowState attached, DisplayFrames displayFrames) {}
-
/**
* Return the layout hints for a newly added window. These values are computed on the
* most recent layout, so they are not guaranteed to be correct.
@@ -1537,13 +1536,6 @@
public void lockNow(Bundle options);
/**
- * Set the last used input method window state. This state is used to make IME transition
- * smooth.
- * @hide
- */
- public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
-
- /**
* An internal callback (from InputMethodManagerService) to notify a state change regarding
* whether the back key should dismiss the software keyboard (IME) or not.
*
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5981ab0..5adc248 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,6 +49,7 @@
import android.util.Slog;
import android.util.StatsLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -75,7 +76,8 @@
* tell the system when we go to sleep so that it can lock the keyguard if needed.
* </p>
*/
-final class Notifier {
+@VisibleForTesting
+public class Notifier {
private static final String TAG = "PowerManagerNotifier";
private static final boolean DEBUG = false;
@@ -188,6 +190,8 @@
try {
mBatteryStats.noteInteractive(true);
} catch (RemoteException ex) { }
+ StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
+ StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
}
/**
@@ -399,6 +403,9 @@
try {
mBatteryStats.noteInteractive(interactive);
} catch (RemoteException ex) { }
+ StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
+ interactive ? StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
+ StatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
// Handle early behaviors.
mInteractive = interactive;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bdf12ca..b3f2a27 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -64,7 +64,6 @@
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
import android.service.vr.IVrManager;
@@ -84,7 +83,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
@@ -230,6 +228,10 @@
private final BatterySaverController mBatterySaverController;
private final BatterySaverStateMachine mBatterySaverStateMachine;
private final BatterySavingStats mBatterySavingStats;
+ private final BinderService mBinderService;
+ private final LocalService mLocalService;
+ private final NativeWrapper mNativeWrapper;
+ private final Injector mInjector;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -636,10 +638,76 @@
}
}
+ /**
+ * Wrapper around the static-native methods of PowerManagerService.
+ *
+ * This class exists to allow us to mock static native methods in our tests. If mocking static
+ * methods becomes easier than this in the future, we can delete this class.
+ */
+ @VisibleForTesting
+ public static class NativeWrapper {
+ /** Wrapper for PowerManager.nativeInit */
+ public void nativeInit(PowerManagerService service) {
+ service.nativeInit();
+ }
+
+ /** Wrapper for PowerManager.nativeAcquireSuspectBlocker */
+ public void nativeAcquireSuspendBlocker(String name) {
+ PowerManagerService.nativeAcquireSuspendBlocker(name);
+ }
+
+ /** Wrapper for PowerManager.nativeReleaseSuspendBlocker */
+ public void nativeReleaseSuspendBlocker(String name) {
+ PowerManagerService.nativeReleaseSuspendBlocker(name);
+ }
+
+ /** Wrapper for PowerManager.nativeSetInteractive */
+ public void nativeSetInteractive(boolean enable) {
+ PowerManagerService.nativeSetInteractive(enable);
+ }
+
+ /** Wrapper for PowerManager.nativeSetAutoSuspend */
+ public void nativeSetAutoSuspend(boolean enable) {
+ PowerManagerService.nativeSetAutoSuspend(enable);
+ }
+
+ /** Wrapper for PowerManager.nativeSendPowerHint */
+ public void nativeSendPowerHint(int hintId, int data) {
+ PowerManagerService.nativeSendPowerHint(hintId, data);
+ }
+
+ /** Wrapper for PowerManager.nativeSetFeature */
+ public void nativeSetFeature(int featureId, int data) {
+ PowerManagerService.nativeSetFeature(featureId, data);
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return new Notifier(looper, context, batteryStats, suspendBlocker, policy);
+ }
+
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ SuspendBlocker suspendBlocker = service.new SuspendBlockerImpl(name);
+ service.mSuspendBlockers.add(suspendBlocker);
+ return suspendBlocker;
+ }
+
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return new BatterySaverPolicy(lock, context, batterySavingStats);
+ }
+
+ NativeWrapper createNativeWrapper() {
+ return new NativeWrapper();
+ }
+ }
+
final Constants mConstants;
private native void nativeInit();
-
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
private static native void nativeSetInteractive(boolean enable);
@@ -648,8 +716,19 @@
private static native void nativeSetFeature(int featureId, int data);
public PowerManagerService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ PowerManagerService(Context context, Injector injector) {
super(context);
+
mContext = context;
+ mBinderService = new BinderService();
+ mLocalService = new LocalService();
+ mNativeWrapper = injector.createNativeWrapper();
+ mInjector = injector;
+
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
mHandlerThread.start();
@@ -658,57 +737,40 @@
mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy = new BatterySaverPolicy(mLock, mContext, mBatterySavingStats);
+ mBatterySaverPolicy =
+ mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
mBatterySaverController = new BatterySaverController(mLock, mContext,
- BackgroundThread.get().getLooper(), mBatterySaverPolicy, mBatterySavingStats);
+ BackgroundThread.get().getLooper(), mBatterySaverPolicy,
+ mBatterySavingStats);
mBatterySaverStateMachine = new BatterySaverStateMachine(
mLock, mContext, mBatterySaverController);
synchronized (mLock) {
- mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
- mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
- mDisplaySuspendBlocker.acquire();
- mHoldingDisplaySuspendBlocker = true;
+ mWakeLockSuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
+ mDisplaySuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+ if (mDisplaySuspendBlocker != null) {
+ mDisplaySuspendBlocker.acquire();
+ mHoldingDisplaySuspendBlocker = true;
+ }
mHalAutoSuspendModeEnabled = false;
mHalInteractiveModeEnabled = true;
mWakefulness = WAKEFULNESS_AWAKE;
-
sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
- nativeInit();
- nativeSetAutoSuspend(false);
- nativeSetInteractive(true);
- nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
+ mNativeWrapper.nativeInit(this);
+ mNativeWrapper.nativeSetAutoSuspend(false);
+ mNativeWrapper.nativeSetInteractive(true);
+ mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
}
}
- @VisibleForTesting
- PowerManagerService(Context context, BatterySaverPolicy batterySaverPolicy) {
- super(context);
-
- mContext = context;
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
- mConstants = new Constants(mHandler);
- mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
- mDisplaySuspendBlocker = null;
- mWakeLockSuspendBlocker = null;
-
- mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy = batterySaverPolicy;
- mBatterySaverController = new BatterySaverController(mLock, context,
- BackgroundThread.getHandler().getLooper(), batterySaverPolicy, mBatterySavingStats);
- mBatterySaverStateMachine = new BatterySaverStateMachine(
- mLock, mContext, mBatterySaverController);
- }
-
@Override
public void onStart() {
- publishBinderService(Context.POWER_SERVICE, new BinderService());
- publishLocalService(PowerManagerInternal.class, new LocalService());
+ publishBinderService(Context.POWER_SERVICE, mBinderService);
+ publishLocalService(PowerManagerInternal.class, mLocalService);
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
@@ -752,11 +814,13 @@
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
mBatteryStats = BatteryStatsService.getService();
- mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
- createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
+ mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
+ mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
- createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
+ mInjector.createSuspendBlocker(
+ this, "PowerManagerService.WirelessChargerDetector"),
mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
@@ -824,7 +888,7 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.DEVICE_DEMO_MODE),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
- IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+ IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE));
if (vrManager != null) {
try {
vrManager.registerListener(mVrStateCallbacks);
@@ -927,7 +991,8 @@
UserHandle.USER_CURRENT) != 0;
if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
mDoubleTapWakeEnabled = doubleTapWakeEnabled;
- nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+ mNativeWrapper.nativeSetFeature(
+ POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
}
}
@@ -1509,6 +1574,11 @@
}
}
+ @VisibleForTesting
+ int getWakefulness() {
+ return mWakefulness;
+ }
+
/**
* Logs the time the device would have spent awake before user activity timeout,
* had the system not been told the user was inactive.
@@ -2640,7 +2710,7 @@
mHalAutoSuspendModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
try {
- nativeSetAutoSuspend(enable);
+ mNativeWrapper.nativeSetAutoSuspend(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -2655,7 +2725,7 @@
mHalInteractiveModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
try {
- nativeSetInteractive(enable);
+ mNativeWrapper.nativeSetInteractive(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -3127,7 +3197,7 @@
break;
}
- nativeSendPowerHint(hintId, data);
+ mNativeWrapper.nativeSendPowerHint(hintId, data);
}
/**
@@ -3718,12 +3788,6 @@
proto.flush();
}
- private SuspendBlocker createSuspendBlockerLocked(String name) {
- SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
- mSuspendBlockers.add(suspendBlocker);
- return suspendBlocker;
- }
-
private void incrementBootCount() {
synchronized (mLock) {
int count;
@@ -4022,7 +4086,7 @@
Slog.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was finalized without being released!");
mReferenceCount = 0;
- nativeReleaseSuspendBlocker(mName);
+ mNativeWrapper.nativeReleaseSuspendBlocker(mName);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
}
} finally {
@@ -4039,7 +4103,7 @@
Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
}
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
- nativeAcquireSuspendBlocker(mName);
+ mNativeWrapper.nativeAcquireSuspendBlocker(mName);
}
}
}
@@ -4052,7 +4116,7 @@
if (DEBUG_SPEW) {
Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
}
- nativeReleaseSuspendBlocker(mName);
+ mNativeWrapper.nativeReleaseSuspendBlocker(mName);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
} else if (mReferenceCount < 0) {
Slog.wtf(TAG, "Suspend blocker \"" + mName
@@ -4090,7 +4154,8 @@
}
}
- private final class BinderService extends IPowerManager.Stub {
+ @VisibleForTesting
+ final class BinderService extends IPowerManager.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -4339,8 +4404,11 @@
@Override // Binder call
public boolean setPowerSaveMode(boolean enabled) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.DEVICE_POWER, null);
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
+ }
final long ident = Binder.clearCallingIdentity();
try {
return setLowPowerModeInternal(enabled);
@@ -4592,6 +4660,16 @@
}
@VisibleForTesting
+ BinderService getBinderServiceInstance() {
+ return mBinderService;
+ }
+
+ @VisibleForTesting
+ LocalService getLocalServiceInstance() {
+ return mLocalService;
+ }
+
+ @VisibleForTesting
// lastRebootReasonProperty argument to permit testing
int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
String line = SystemProperties.get(lastRebootReasonProperty);
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 180f343..8070f3a 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -23,45 +23,79 @@
import android.os.SharedMemory;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.Pair;
+import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
-import android.util.Pair;
-import android.util.Slog;
+import android.util.apk.VerityBuilder;
+
+import libcore.util.HexEncoding;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.security.DigestException;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import sun.security.pkcs.PKCS7;
+
/** Provides fsverity related operations. */
abstract public class VerityUtils {
private static final String TAG = "VerityUtils";
+ /** The maximum size of signature file. This is just to avoid potential abuse. */
+ private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
+
private static final boolean DEBUG = false;
/**
- * Generates Merkle tree and fsverity metadata.
+ * Generates Merkle tree and fs-verity metadata.
*
- * @return {@code SetupResult} that contains the {@code EsetupResultCode}, and when success, the
+ * @return {@code SetupResult} that contains the result code, and when success, the
* {@code FileDescriptor} to read all the data from.
*/
- public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {
- if (DEBUG) Slog.d(TAG, "Trying to install apk verity to " + apkPath);
+ public static SetupResult generateApkVeritySetupData(@NonNull String apkPath,
+ String signaturePath, boolean skipSigningBlock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Trying to install apk verity to " + apkPath + " with signature file "
+ + signaturePath);
+ }
SharedMemory shm = null;
try {
- byte[] signedRootHash = ApkSignatureVerifier.getVerityRootHash(apkPath);
- if (signedRootHash == null) {
+ byte[] signedVerityHash;
+ if (skipSigningBlock) {
+ signedVerityHash = ApkSignatureVerifier.getVerityRootHash(apkPath);
+ } else {
+ Path path = Paths.get(signaturePath);
+ if (Files.exists(path)) {
+ // TODO(112037636): fail early if the signing key is not in .fs-verity keyring.
+ PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(path));
+ signedVerityHash = pkcs7.getContentInfo().getContentBytes();
+ if (DEBUG) {
+ Slog.d(TAG, "fs-verity measurement = " + bytesToString(signedVerityHash));
+ }
+ } else {
+ signedVerityHash = null;
+ }
+ }
+
+ if (signedVerityHash == null) {
if (DEBUG) {
- Slog.d(TAG, "Skip verity tree generation since there is no root hash");
+ Slog.d(TAG, "Skip verity tree generation since there is no signed root hash");
}
return SetupResult.skipped();
}
- Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath,
- signedRootHash);
+ Pair<SharedMemory, Integer> result = generateFsVerityIntoSharedMemory(apkPath,
+ signaturePath, signedVerityHash, skipSigningBlock);
shm = result.first;
int contentSize = result.second;
FileDescriptor rfd = shm.getFileDescriptor();
@@ -81,11 +115,11 @@
}
/**
- * {@see ApkSignatureVerifier#generateFsverityRootHash(String)}.
+ * {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
*/
- public static byte[] generateFsverityRootHash(@NonNull String apkPath)
+ public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
- return ApkSignatureVerifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
}
/**
@@ -97,22 +131,114 @@
}
/**
+ * Generates fs-verity metadata for {@code filePath} in the buffer created by {@code
+ * trackedBufferFactory}. The metadata contains the Merkle tree, fs-verity descriptor and
+ * extensions, including a PKCS#7 signature provided in {@code signaturePath}.
+ *
+ * <p>It is worthy to note that {@code trackedBufferFactory} generates a "tracked" {@code
+ * ByteBuffer}. The data will be used outside this method via the factory itself.
+ *
+ * @return fs-verity measurement of {@code filePath}, which is a SHA-256 of fs-verity descriptor
+ * and authenticated extensions.
+ */
+ private static byte[] generateFsverityMetadata(String filePath, String signaturePath,
+ @NonNull TrackedShmBufferFactory trackedBufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
+ VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
+ file, trackedBufferFactory);
+
+ ByteBuffer buffer = result.verityData;
+ buffer.position(result.merkleTreeSize);
+ return generateFsverityDescriptorAndMeasurement(file, result.rootHash, signaturePath,
+ buffer);
+ }
+ }
+
+ /**
+ * Generates fs-verity descriptor including the extensions to the {@code output} and returns the
+ * fs-verity measurement.
+ *
+ * @return fs-verity measurement, which is a SHA-256 of fs-verity descriptor and authenticated
+ * extensions.
+ */
+ private static byte[] generateFsverityDescriptorAndMeasurement(
+ @NonNull RandomAccessFile file, @NonNull byte[] rootHash,
+ @NonNull String pkcs7SignaturePath, @NonNull ByteBuffer output)
+ throws IOException, NoSuchAlgorithmException, DigestException {
+ final short kRootHashExtensionId = 1;
+ final short kPkcs7SignatureExtensionId = 3;
+ final int origPosition = output.position();
+
+ // For generating fs-verity file measurement, which consists of the descriptor and
+ // authenticated extensions (but not unauthenticated extensions and the footer).
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+ // 1. Generate fs-verity descriptor.
+ final byte[] desc = constructFsverityDescriptorNative(file.length());
+ output.put(desc);
+ md.update(desc);
+
+ // 2. Generate authenticated extensions.
+ final byte[] authExt =
+ constructFsverityExtensionNative(kRootHashExtensionId, rootHash.length);
+ output.put(authExt);
+ output.put(rootHash);
+ md.update(authExt);
+ md.update(rootHash);
+
+ // 3. Generate unauthenticated extensions.
+ ByteBuffer header = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
+ output.putShort((short) 1); // number of unauthenticated extensions below
+ output.position(output.position() + 6);
+
+ // Generate PKCS#7 extension. NB: We do not verify agaist trusted certificate (should be
+ // done by the caller if needed).
+ Path path = Paths.get(pkcs7SignaturePath);
+ if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new IllegalArgumentException("Signature size is unexpectedly large: "
+ + pkcs7SignaturePath);
+ }
+ final byte[] pkcs7Signature = Files.readAllBytes(path);
+ output.put(constructFsverityExtensionNative(kPkcs7SignatureExtensionId,
+ pkcs7Signature.length));
+ output.put(pkcs7Signature);
+
+ // 4. Generate the footer.
+ output.put(constructFsverityFooterNative(output.position() - origPosition));
+
+ return md.digest();
+ }
+
+ private static native byte[] constructFsverityDescriptorNative(long fileSize);
+ private static native byte[] constructFsverityExtensionNative(short extensionId,
+ int extensionDataSize);
+ private static native byte[] constructFsverityFooterNative(int offsetToDescriptorHead);
+
+ /**
* Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains
* Merkle tree and fsverity headers for the given apk, in the form that can immediately be used
* for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has
* length equals to the returned {@code Integer}.
*/
- private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory(
- String apkPath, byte[] expectedRootHash)
+ private static Pair<SharedMemory, Integer> generateFsVerityIntoSharedMemory(
+ String apkPath, String signaturePath, @NonNull byte[] expectedRootHash,
+ boolean skipSigningBlock)
throws IOException, SecurityException, DigestException, NoSuchAlgorithmException,
SignatureNotFoundException {
TrackedShmBufferFactory shmBufferFactory = new TrackedShmBufferFactory();
- byte[] generatedRootHash = ApkSignatureVerifier.generateApkVerity(apkPath,
- shmBufferFactory);
+ byte[] generatedRootHash;
+ if (skipSigningBlock) {
+ generatedRootHash = ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory);
+ } else {
+ generatedRootHash = generateFsverityMetadata(apkPath, signaturePath, shmBufferFactory);
+ }
// We only generate Merkle tree once here, so it's important to make sure the root hash
// matches the signed one in the apk.
if (!Arrays.equals(expectedRootHash, generatedRootHash)) {
- throw new SecurityException("Locally generated verity root hash does not match");
+ throw new SecurityException("verity hash mismatch: "
+ + bytesToString(generatedRootHash) + " != " + bytesToString(expectedRootHash));
}
int contentSize = shmBufferFactory.getBufferLimit();
@@ -126,11 +252,15 @@
return Pair.create(shm, contentSize);
}
+ private static String bytesToString(byte[] bytes) {
+ return HexEncoding.encodeToString(bytes);
+ }
+
public static class SetupResult {
/** Result code if verity is set up correctly. */
private static final int RESULT_OK = 1;
- /** Result code if the apk does not contain a verity root hash. */
+ /** Result code if signature is not provided. */
private static final int RESULT_SKIPPED = 2;
/** Result code if the setup failed. */
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 5e3fe0a..d0de940 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
@@ -63,10 +64,13 @@
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.IProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
@@ -78,10 +82,12 @@
import com.android.internal.os.KernelWakelockStats;
import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
+import com.android.internal.os.StoragedUidIoStatsReader;
import com.android.internal.util.DumpUtils;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -95,6 +101,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -123,7 +130,7 @@
public static final String CONFIG_DIR = "/data/misc/stats-service";
static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
public static final int CODE_DATA_BROADCAST = 1;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -172,14 +179,18 @@
new KernelUidCpuActiveTimeReader();
private KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
new KernelUidCpuClusterTimeReader();
+ private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
+ new StoragedUidIoStatsReader();
private static IThermalService sThermalService;
+ private File mBaseDir =
+ new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
public StatsCompanionService(Context context) {
super();
mContext = context;
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
+ mBaseDir.mkdirs();
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -966,6 +977,7 @@
e.writeLong(processMemoryState.rssInBytes);
e.writeLong(processMemoryState.cacheInBytes);
e.writeLong(processMemoryState.swapInBytes);
+ e.writeLong(processMemoryState.rssHighWatermarkInBytes);
pulledData.add(e);
}
}
@@ -1031,7 +1043,7 @@
looperStats.reset();
for (LooperStats.ExportedEntry entry : entries) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(1000); // uid collection not implemented yet
+ e.writeInt(entry.workSourceUid);
e.writeString(entry.handlerClassName);
e.writeString(entry.threadName);
e.writeString(entry.messageName);
@@ -1244,6 +1256,107 @@
Binder.restoreCallingIdentity(token);
}
+ long mLastProcStatsHighWaterMark = readProcStatsHighWaterMark();
+
+ private long readProcStatsHighWaterMark() {
+ try {
+ File[] files = mBaseDir.listFiles();
+ if (files == null || files.length == 0) {
+ return 0;
+ }
+ if (files.length > 1) {
+ Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
+ }
+ return Long.valueOf(files[0].getName());
+ } catch (SecurityException e) {
+ Log.e(TAG, "Failed to get procstats high watermark file.", e);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Failed to parse file name.", e);
+ }
+ return 0;
+ }
+
+ private IProcessStats mProcessStats =
+ IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
+
+ private void pullProcessStats(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ try {
+ List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+ long highWaterMark = mProcessStats.getCommittedStats(
+ mLastProcStatsHighWaterMark, ProcessStats.REPORT_ALL, true, statsFiles);
+ if (statsFiles.size() != 1) {
+ return;
+ }
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
+ int[] len = new int[1];
+ byte[] stats = readFully(stream, len);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeStorage(Arrays.copyOf(stats, len[0]));
+ pulledData.add(e);
+ new File(mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).delete();
+ mLastProcStatsHighWaterMark = highWaterMark;
+ new File(
+ mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ }
+ }
+
+ static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
+ int pos = 0;
+ final int initialAvail = stream.available();
+ byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384];
+ while (true) {
+ int amt = stream.read(data, pos, data.length - pos);
+ if (DEBUG) {
+ Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length);
+ }
+ if (amt < 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length);
+ }
+ outLen[0] = pos;
+ return data;
+ }
+ pos += amt;
+ if (pos >= data.length) {
+ byte[] newData = new byte[pos + 16384];
+ if (DEBUG) {
+ Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length);
+ }
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
+ private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
+ fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
+ fgFsync, bgFsync) -> {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(uid);
+ e.writeLong(fgCharsRead);
+ e.writeLong(fgCharsWrite);
+ e.writeLong(fgBytesRead);
+ e.writeLong(fgBytesWrite);
+ e.writeLong(bgCharsRead);
+ e.writeLong(bgCharsWrite);
+ e.writeLong(bgBytesRead);
+ e.writeLong(bgBytesWrite);
+ e.writeLong(fgFsync);
+ e.writeLong(bgFsync);
+ pulledData.add(e);
+ });
+ }
+
/**
* Pulls various data.
*/
@@ -1357,6 +1470,14 @@
pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.PROC_STATS: {
+ pullProcessStats(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DISK_IO: {
+ pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
@@ -1367,13 +1488,13 @@
@Override // Binder call
public void statsdReady() {
enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "learned that statsdReady");
+ if (DEBUG) {
+ Slog.d(TAG, "learned that statsdReady");
+ }
sayHiToStatsd(); // tell statsd that we're ready too and link to it
- mContext.sendBroadcastAsUser(
- new Intent(StatsManager.ACTION_STATSD_STARTED)
+ mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.SYSTEM,
- android.Manifest.permission.DUMP);
+ UserHandle.SYSTEM, android.Manifest.permission.DUMP);
}
@Override
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c2d8188..79eab6b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -565,11 +565,12 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.showBiometricDialog(bundle, receiver, type);
+ mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 83075ed..bcf9212 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -22,17 +22,23 @@
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
+import android.content.IIntentSender;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
import android.util.SparseIntArray;
-import android.view.RemoteAnimationAdapter;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.PendingIntentRecord;
+import com.android.server.am.SafeActivityOptions;
+import com.android.server.am.TaskRecord;
import com.android.server.am.WindowProcessController;
+import java.lang.ref.WeakReference;
import java.util.List;
/**
@@ -177,6 +183,27 @@
int userId, Intent[] intents, Bundle bOptions);
/**
+ * Start intents as a package.
+ *
+ * @param uid Make a call as if this UID did.
+ * @param callingPackage Make a call as if this package did.
+ * @param intents Intents to start.
+ * @param userId Start the intents on this user.
+ * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+ * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+ * null if not originated by PendingIntent
+ */
+ public abstract int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+ String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+ boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent);
+
+ public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
+ String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+ int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+ PendingIntentRecord originatingPendingIntent);
+
+ /**
* Start activity {@code intent} without calling user-id check.
*
* - DO NOT call it with the calling UID cleared.
@@ -267,6 +294,9 @@
public abstract void onProcessRemoved(String name, int uid);
public abstract void onCleanUpApplicationRecord(WindowProcessController proc);
public abstract int getTopProcessState();
+ public abstract boolean isHeavyWeightProcess(WindowProcessController proc);
+ public abstract void clearHeavyWeightProcessIfEquals(WindowProcessController proc);
+ public abstract void finishHeavyWeightApp();
public abstract boolean isSleeping();
public abstract boolean isShuttingDown();
@@ -281,4 +311,25 @@
public abstract void onPackageDataCleared(String name);
public abstract void onPackageUninstalled(String name);
+ public abstract void onPackageAdded(String name, boolean replacing);
+
+ public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai);
+
+ /**
+ * Set the corresponding display information for the process global configuration. To be called
+ * when we need to show IME on a different display.
+ *
+ * @param pid The process id associated with the IME window.
+ * @param displayId The ID of the display showing the IME.
+ */
+ public abstract void onImeWindowSetOnDisplay(int pid, int displayId);
+
+ public abstract void sendActivityResult(int callingUid, IBinder activityToken,
+ String resultWho, int requestCode, int resultCode, Intent data);
+ public abstract void clearPendingResultForActivity(
+ IBinder activityToken, WeakReference<PendingIntentRecord> pir);
+ public abstract IIntentSender getIntentSender(int type, String packageName,
+ int callingUid, int userId, IBinder token, String resultWho,
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
+ Bundle bOptions);
}
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 00e3050..be8a0bd 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -35,12 +35,6 @@
long STATUS_BAR_TRANSITION_DURATION = 120L;
/**
- * @return Whether we should detach the wallpaper during the animation.
- * @see Animation#setDetachWallpaper
- */
- boolean getDetachWallpaper();
-
- /**
* @return Whether we should show the wallpaper during the animation.
* @see Animation#getShowWallpaper()
*/
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 6da9f10..fc76102 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1632,17 +1632,6 @@
return null;
}
- int getLowestAnimLayer() {
- for (int i = 0; i < mChildren.size(); i++) {
- final WindowState w = mChildren.get(i);
- if (w.mRemoved) {
- continue;
- }
- return w.mWinAnimator.mAnimLayer;
- }
- return Integer.MAX_VALUE;
- }
-
WindowState getHighestAnimLayerWindow(WindowState currentTarget) {
WindowState candidate = null;
for (int i = mChildren.indexOf(currentTarget); i >= 0; i--) {
@@ -1650,8 +1639,7 @@
if (w.mRemoved) {
continue;
}
- if (candidate == null || w.mWinAnimator.mAnimLayer >
- candidate.mWinAnimator.mAnimLayer) {
+ if (candidate == null) {
candidate = w;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6c7304d..a762fe9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -76,6 +76,7 @@
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_BOOT;
@@ -150,7 +151,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
-import com.android.internal.view.IInputMethodClient;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -439,36 +439,12 @@
return;
}
- final int flags = w.mAttrs.flags;
-
- // If this window is animating, make a note that we have an animating window and take
- // care of a request to run a detached wallpaper animation.
- if (winAnimator.isAnimationSet()) {
- final AnimationAdapter anim = w.getAnimation();
- if (anim != null) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0 && anim.getDetachWallpaper()) {
- mTmpWindow = w;
- }
- final int color = anim.getBackgroundColor();
- if (color != 0) {
- final TaskStack stack = w.getStack();
- if (stack != null) {
- stack.setAnimationBackground(winAnimator, color);
- }
- }
- }
- }
-
- // If this window's app token is running a detached wallpaper animation, make a note so
- // we can ensure the wallpaper is displayed behind it.
- final AppWindowToken atoken = winAnimator.mWin.mAppToken;
- final AnimationAdapter animation = atoken != null ? atoken.getAnimation() : null;
- if (animation != null) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0 && animation.getDetachWallpaper()) {
- mTmpWindow = w;
- }
-
- final int color = animation.getBackgroundColor();
+ // If this window is animating, ensure the animation background is set.
+ final AnimationAdapter anim = w.mAppToken != null
+ ? w.mAppToken.getAnimation()
+ : w.getAnimation();
+ if (anim != null) {
+ final int color = anim.getBackgroundColor();
if (color != 0) {
final TaskStack stack = w.getStack();
if (stack != null) {
@@ -572,7 +548,7 @@
if (!w.mLayoutAttached) {
if (mTmpInitial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.mContentChanged = false;
+ w.resetContentChanged();
}
if (w.mAttrs.type == TYPE_DREAM) {
// Don't layout windows behind a dream, so that if it does stuff like hide
@@ -617,7 +593,7 @@
|| w.mLayoutNeeded) {
if (mTmpInitial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.mContentChanged = false;
+ w.resetContentChanged();
}
w.mLayoutNeeded = false;
w.prelayout();
@@ -700,7 +676,7 @@
final WindowStateAnimator winAnimator = w.mWinAnimator;
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ w.resetContentChanged();
// Moved from updateWindowsAndWallpaperLocked().
if (w.mHasSurface) {
@@ -2307,21 +2283,6 @@
mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
}
- /**
- * If a window that has an animation specifying a colored background and the current wallpaper
- * is visible, then the color goes *below* the wallpaper so we don't cause the wallpaper to
- * suddenly disappear.
- */
- int getLayerForAnimationBackground(WindowStateAnimator winAnimator) {
- final WindowState visibleWallpaper = mBelowAppWindowsContainers.getWindow(
- w -> w.mIsWallpaper && w.isVisibleNow());
-
- if (visibleWallpaper != null) {
- return visibleWallpaper.mWinAnimator.mAnimLayer;
- }
- return winAnimator.mAnimLayer;
- }
-
void prepareFreezingTaskBounds() {
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -2410,6 +2371,7 @@
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
+ proto.write(SURFACE_SIZE, mSurfaceSize);
proto.end(token);
}
@@ -2651,6 +2613,12 @@
*/
void setInputMethodWindowLocked(WindowState win) {
mInputMethodWindow = win;
+ // Update display configuration for IME process.
+ if (mInputMethodWindow != null) {
+ final int imePid = mInputMethodWindow.mSession.mPid;
+ mService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
+ mInputMethodWindow.getDisplayId());
+ }
computeImeTarget(true /* updateImeTarget */);
}
@@ -2739,22 +2707,13 @@
if (highestTarget != null) {
final AppTransition appTransition = mService.mAppTransition;
if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, appTransition + " " + highestTarget
- + " animating=" + highestTarget.mWinAnimator.isAnimationSet()
- + " layer=" + highestTarget.mWinAnimator.mAnimLayer
- + " new layer=" + target.mWinAnimator.mAnimLayer);
+ + " animating=" + highestTarget.isAnimating());
if (appTransition.isTransitionSet()) {
// If we are currently setting up for an animation, hold everything until we
// can find out what will happen.
setInputMethodTarget(highestTarget, true);
return highestTarget;
- } else if (highestTarget.mWinAnimator.isAnimationSet() &&
- highestTarget.mWinAnimator.mAnimLayer > target.mWinAnimator.mAnimLayer) {
- // If the window we are currently targeting is involved with an animation,
- // and it is on top of the next target we will be over, then hold off on
- // moving until that is done.
- setInputMethodTarget(highestTarget, true);
- return highestTarget;
}
}
}
@@ -2927,29 +2886,19 @@
return false;
}
- void updateWindowsForAnimator(WindowAnimator animator) {
- mTmpWindowAnimator = animator;
+ void updateWindowsForAnimator() {
forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */);
}
- void updateWallpaperForAnimator(WindowAnimator animator) {
+ /**
+ * Updates the {@link TaskStack#setAnimationBackground} for all windows.
+ */
+ void updateBackgroundForAnimator() {
resetAnimationBackgroundAnimator();
-
- // Used to indicate a detached wallpaper.
- mTmpWindow = null;
- mTmpWindowAnimator = animator;
-
forAllWindows(mUpdateWallpaperForAnimator, true /* traverseTopToBottom */);
-
- if (animator.mWindowDetachedWallpaper != mTmpWindow) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Detached wallpaper changed from "
- + animator.mWindowDetachedWallpaper + " to " + mTmpWindow);
- animator.mWindowDetachedWallpaper = mTmpWindow;
- animator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- }
}
- boolean inputMethodClientHasFocus(IInputMethodClient client) {
+ boolean isInputMethodClientFocus(int uid, int pid) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
return false;
@@ -2961,17 +2910,13 @@
Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
}
- final IInputMethodClient imeClient = imFocus.mSession.mClient;
-
if (DEBUG_INPUT_METHOD) {
- Slog.i(TAG_WM, "IM target client: " + imeClient);
- if (imeClient != null) {
- Slog.i(TAG_WM, "IM target client binder: " + imeClient.asBinder());
- Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
- }
+ Slog.i(TAG_WM, "IM target uid/pid: " + imFocus.mSession.mUid
+ + "/" + imFocus.mSession.mPid);
+ Slog.i(TAG_WM, "Requesting client uid/pid: " + uid + "/" + pid);
}
- return imeClient != null && imeClient.asBinder() == client.asBinder();
+ return imFocus.mSession.mUid == uid && imFocus.mSession.mPid == pid;
}
boolean hasSecureWindowOnScreen() {
@@ -3156,6 +3101,10 @@
}
}
+ int getSurfaceSize() {
+ return mSurfaceSize;
+ }
+
void performLayout(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
@@ -3256,7 +3205,7 @@
// TODO(b/68392460): We should screenshot Task controls directly
// but it's difficult at the moment as the Task doesn't have the
// correct size set.
- final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot);
+ final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, inRotation, rot);
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
return null;
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index d89d6f0..77a024c 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -44,11 +44,6 @@
}
@Override
- public boolean getDetachWallpaper() {
- return mSpec.getDetachWallpaper();
- }
-
- @Override
public boolean getShowWallpaper() {
return mSpec.getShowWallpaper();
}
@@ -98,13 +93,6 @@
interface AnimationSpec {
/**
- * @see AnimationAdapter#getDetachWallpaper
- */
- default boolean getDetachWallpaper() {
- return false;
- }
-
- /**
* @see AnimationAdapter#getShowWallpaper
*/
default boolean getShowWallpaper() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 1eae567..6fef1630 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,8 +20,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
-
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
@@ -48,16 +48,13 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.input.InputWindowHandle;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
-
import com.google.android.collect.Sets;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -93,6 +90,7 @@
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mTargetAppToken;
+ private int mTargetActivityType;
private Rect mMinimizedHomeBounds = new Rect();
// We start the RecentsAnimationController in a pending-start state since we need to wait for
@@ -259,23 +257,37 @@
mDisplayId = displayId;
}
+ public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
+ initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds);
+ }
+
/**
* Initializes the recents animation controller. This is a separate call from the constructor
* because it may call cancelAnimation() which needs to properly clean up the controller
* in the window manager.
*/
- public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
- // Make leashes for each of the visible tasks and add it to the recents animation to be
- // started
- final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+ @VisibleForTesting
+ void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) {
+ mTargetActivityType = targetActivityType;
+
+ // Make leashes for each of the visible/target tasks and add it to the recents animation to
+ // be started
final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
+ final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType);
+ if (targetStack != null) {
+ for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
+ final Task t = targetStack.getChildAt(i);
+ if (!visibleTasks.contains(t)) {
+ visibleTasks.add(t);
+ }
+ }
+ }
final int taskCount = visibleTasks.size();
for (int i = 0; i < taskCount; i++) {
final Task task = visibleTasks.get(i);
final WindowConfiguration config = task.getWindowConfiguration();
if (config.tasksAreFloating()
- || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || config.getActivityType() == targetActivityType) {
+ || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
addAnimation(task, !recentTaskIds.get(task.mTaskId));
@@ -586,7 +598,10 @@
final Rect insets = new Rect();
mainWindow.getContentInsets(insets);
InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
- mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
+ final int mode = topApp.getActivityType() == mTargetActivityType
+ ? MODE_OPENING
+ : MODE_CLOSING;
+ mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
!topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
@@ -594,11 +609,6 @@
}
@Override
- public boolean getDetachWallpaper() {
- return false;
- }
-
- @Override
public boolean getShowWallpaper() {
return false;
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 00422e3..8ec0a01 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -325,11 +325,6 @@
}
@Override
- public boolean getDetachWallpaper() {
- return false;
- }
-
- @Override
public boolean getShowWallpaper() {
return false;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d92818a..a9571be 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -48,7 +48,6 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
import static com.android.server.wm.WindowManagerService.logSurface;
-import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
@@ -91,7 +90,6 @@
private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
- private boolean mWallpaperForceHidingChanged = false;
private Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private float mScreenBrightness = -1;
@@ -626,18 +624,6 @@
recentsAnimationController.checkAnimationReady(mWallpaperController);
}
- if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
- && !mService.mAppTransition.isReady()) {
- // At this point, there was a window with a wallpaper that was force hiding other
- // windows behind it, but now it is going away. This may be simple -- just animate away
- // the wallpaper and its window -- or it may be hard -- the wallpaper now needs to be
- // shown behind something that was hidden.
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
- "after animateAwayWallpaperLocked", defaultDisplay.pendingLayoutChanges);
- }
- mWallpaperForceHidingChanged = false;
-
if (mWallpaperMayChange) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
@@ -961,10 +947,6 @@
mWallpaperMayChange = true;
doRequest = true;
}
- if ((bulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0) {
- mWallpaperForceHidingChanged = true;
- doRequest = true;
- }
if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
mOrientationChangeComplete = false;
} else {
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 6f597277..95ca0a6 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -16,12 +16,12 @@
package com.android.server.wm;
-import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl.Transaction;
@@ -33,10 +33,10 @@
/**
* Helper class for seamless rotation.
*
- * Works by transforming the window token back into the old display rotation.
+ * Works by transforming the {@link WindowState} back into the old display rotation.
*
- * Uses deferTransactionUntil instead of latching on the buffer size to allow for seamless 180
- * degree rotations.
+ * Uses {@link android.view.SurfaceControl#deferTransactionUntil(Surface, long)} instead of
+ * latching on the buffer size to allow for seamless 180 degree rotations.
*/
public class SeamlessRotator {
@@ -45,7 +45,7 @@
private final int mOldRotation;
private final int mNewRotation;
- public SeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) {
+ public SeamlessRotator(@Rotation int oldRotation, @Rotation int newRotation, DisplayInfo info) {
mOldRotation = oldRotation;
mNewRotation = newRotation;
@@ -60,11 +60,16 @@
}
/**
- * Applies a transform to the window token's surface that undoes the effect of the global
+ * Applies a transform to the {@link WindowState} surface that undoes the effect of the global
* display rotation.
*/
- public void unrotate(Transaction transaction, WindowToken token) {
- transaction.setMatrix(token.getSurfaceControl(), mTransform, mFloat9);
+ public void unrotate(Transaction transaction, WindowState win) {
+ transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
+
+ // WindowState sets the position of the window so transform the position and update it.
+ final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
+ mTransform.mapPoints(winSurfacePos);
+ transaction.setPosition(win.getSurfaceControl(), winSurfacePos[0], winSurfacePos[1]);
}
/**
@@ -78,27 +83,23 @@
}
/**
- * Removes the transform to the window token's surface that undoes the effect of the global
- * display rotation.
+ * Removes the transform and sets the previously known surface position for {@link WindowState}
+ * surface that undoes the effect of the global display rotation.
*
- * Removing the transform and the result of the WindowState's layout are both tied to the
- * WindowState's next frame, such that they apply at the same time the client draws the
+ * 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.
*/
- public void finish(WindowToken token, WindowState win) {
+ public void finish(WindowState win) {
mTransform.reset();
- token.getPendingTransaction().setMatrix(token.mSurfaceControl, mTransform, mFloat9);
+ 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) {
- token.getPendingTransaction().deferTransactionUntil(token.mSurfaceControl,
- win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
- win.getFrameNumber());
- win.getPendingTransaction().deferTransactionUntil(win.mSurfaceControl,
- win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
- win.getFrameNumber());
- win.getPendingTransaction().deferTransactionUntil(
- win.mWinAnimator.mSurfaceController.mSurfaceControl,
- win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
- win.getFrameNumber());
+ t.deferTransactionUntil(win.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
+ t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index f9a71d3..acc9c03 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.util.MergedConfiguration;
@@ -56,10 +55,6 @@
import android.view.WindowManager;
import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -73,8 +68,6 @@
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
- final IInputMethodClient mClient;
- final InputMethodManagerInternal mInputMethodManagerInternal;
final int mUid;
final int mPid;
private final String mStringName;
@@ -95,17 +88,9 @@
private String mPackageName;
private String mRelayoutTag;
- public Session(WindowManagerService service, IWindowSessionCallback callback,
- IInputMethodClient client, IInputContext inputContext) {
+ public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
mCallback = callback;
- mClient = client;
- // Depending on the timing when Session object gets called and SystemServer#mFactoryTestMode
- // this could be null, right?
- final InputMethodManagerInternal immInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- mInputMethodManagerInternal =
- immInternal != null ? immInternal : InputMethodManagerInternal.NOP;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
@@ -134,12 +119,11 @@
sb.append("}");
mStringName = sb.toString();
- mInputMethodManagerInternal.addClient(client, inputContext, mUid, mPid);
try {
- client.asBinder().linkToDeath(this, 0);
+ mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
// The caller has died, so we can just forget about this.
- mInputMethodManagerInternal.removeClient(client);
+ // Hmmm, should we call killSessionLocked()??
}
}
@@ -159,9 +143,8 @@
@Override
public void binderDied() {
- mInputMethodManagerInternal.removeClient(mClient);
synchronized(mService.mWindowMap) {
- mClient.asBinder().unlinkToDeath(this, 0);
+ mCallback.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
killSessionLocked();
}
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index c99329a..0c9a14b 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -21,7 +21,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -35,7 +35,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eb419c9..6aa0e01 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,6 +29,8 @@
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
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;
@@ -297,6 +299,12 @@
return boundsChange;
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ updateSurfaceSize(dc);
+ super.onDisplayChanged(dc);
+ }
+
/**
* Sets the bounds used to calculate the insets. See
* {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
@@ -553,9 +561,10 @@
@Override
public SurfaceControl getAnimationLeashParent() {
- // Reparent to the animation layer so that we aren't clipped by the non-minimized
- // stack bounds, currently we only animate the task for the recents animation
- return getAppAnimationLayer(ANIMATION_LAYER_STANDARD);
+ // Currently, only the recents animation will create animation leashes for tasks. In this
+ // case, reparent the task to the home animation layer while it is being animated to allow
+ // the home activity to reorder the app windows relative to its own.
+ return getAppAnimationLayer(ANIMATION_LAYER_HOME);
}
boolean isTaskAnimating() {
@@ -703,6 +712,8 @@
getBounds().writeToProto(proto, BOUNDS);
mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
proto.write(DEFER_REMOVAL, mDeferRemoval);
+ proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
+ proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 2b84937..00caceb 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1070,11 +1070,8 @@
}
void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
- int animLayer = winAnimator.mAnimLayer;
- if (mAnimationBackgroundAnimator == null
- || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
+ if (mAnimationBackgroundAnimator == null) {
mAnimationBackgroundAnimator = winAnimator;
- animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
showAnimationSurface(((color >> 24) & 0xff) / 255f);
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3d349ce..a448f97 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -120,13 +120,11 @@
}
mFindResults.resetTopWallpaper = true;
- if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
+ if (w.mAppToken != null && w.mAppToken.isHidden() && !w.mAppToken.isSelfAnimating()) {
+
// If this window's app token is hidden and not animating, it is of no interest to us.
- if (w.mAppToken.isHidden() && !w.mAppToken.isSelfAnimating()) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
- "Skipping hidden and not animating token: " + w);
- return false;
- }
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
+ return false;
}
if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
+ " mDrawState=" + w.mWinAnimator.mDrawState);
@@ -177,7 +175,7 @@
&& (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
- if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) {
+ if (w == mWallpaperTarget && w.isAnimating()) {
// The current wallpaper target is animating, so we'll look behind it for
// another possible target and figure out what is going on later.
if (DEBUG_WALLPAPER) Slog.v(TAG,
@@ -185,10 +183,6 @@
}
// Found a target! End search.
return true;
- } else if (w == winAnimator.mWindowDetachedWallpaper) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "Found animating detached wallpaper target win: " + w);
- mFindResults.setUseTopWallpaperAsTarget(true);
}
return false;
};
@@ -243,7 +237,7 @@
}
boolean isWallpaperTargetAnimating() {
- return mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimationSet()
+ return mWallpaperTarget != null && mWallpaperTarget.isAnimating()
&& (mWallpaperTarget.mAppToken == null
|| !mWallpaperTarget.mAppToken.isWaitingForTransitionStart());
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index ddda027..e15b783 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -138,7 +138,7 @@
wallpaper.dispatchWallpaperVisibility(visible);
if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
- + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
+ + wallpaper);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 825255e..98c77ac 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -72,11 +72,6 @@
}
@Override
- public boolean getDetachWallpaper() {
- return mAnimation.getDetachWallpaper();
- }
-
- @Override
public boolean getShowWallpaper() {
return mAnimation.getShowWallpaper();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 793ffce..ad0b8ec 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -58,17 +58,6 @@
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
- boolean mAppWindowAnimating;
- /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
- * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
- int mAnimTransactionSequence;
-
- /** Window currently running an animation that has requested it be detached
- * from the wallpaper. This means we need to ensure the wallpaper is
- * visible behind it in case it animates in a way that would allow it to be
- * seen. If multiple windows satisfy this, use the lowest window. */
- WindowState mWindowDetachedWallpaper = null;
-
int mBulkUpdateParams = 0;
Object mLastWindowFreezeSource;
@@ -191,9 +180,8 @@
// Update animations of all applications, including those
// associated with exiting/removed apps
- ++mAnimTransactionSequence;
- dc.updateWindowsForAnimator(this);
- dc.updateWallpaperForAnimator(this);
+ dc.updateWindowsForAnimator();
+ dc.updateBackgroundForAnimator();
dc.prepareSurfaces();
}
@@ -284,9 +272,6 @@
if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
builder.append(" WALLPAPER_MAY_CHANGE");
}
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED) != 0) {
- builder.append(" FORCE_HIDING_CHANGED");
- }
if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
builder.append(" ORIENTATION_CHANGE_COMPLETE");
}
@@ -317,8 +302,6 @@
pw.println();
if (dumpAll) {
- pw.print(prefix); pw.print("mAnimTransactionSequence=");
- pw.print(mAnimTransactionSequence);
pw.print(prefix); pw.print("mCurrentTime=");
pw.println(TimeUtils.formatUptime(mCurrentTime));
}
@@ -327,10 +310,6 @@
pw.print(Integer.toHexString(mBulkUpdateParams));
pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
}
- if (mWindowDetachedWallpaper != null) {
- pw.print(prefix); pw.print("mWindowDetachedWallpaper=");
- pw.println(mWindowDetachedWallpaper);
- }
}
int getPendingLayoutChanges(final int displayId) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8e704a8..4883f97 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -20,13 +20,14 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.SurfaceControl.Transaction;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.CallSuper;
import android.annotation.IntDef;
@@ -41,10 +42,8 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
-
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
-
import java.io.PrintWriter;
import java.util.Comparator;
import java.util.LinkedList;
@@ -70,7 +69,8 @@
/**
* Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
- * activities that happens below all {@link TaskStack}s.
+ * activities and all activities that are being controlled by the recents animation. This
+ * layer is generally below all {@link TaskStack}s.
*/
static final int ANIMATION_LAYER_HOME = 2;
@@ -503,6 +503,24 @@
}
}
+ /**
+ * 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);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index cbe7d9d..20a874b 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -36,7 +36,6 @@
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
-import android.view.WindowManager;
import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -191,6 +190,10 @@
private final Rect mTmpRect = new Rect();
+ private boolean mHasOutsets;
+
+ private boolean mContentChanged;
+
public WindowFrames() {
}
@@ -237,11 +240,9 @@
* Calculates the outsets for this windowFrame. The outsets are calculated by the area between
* the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
* {@link #mOutsets} is set to empty.
- *
- * @param hasOutsets Whether this frame has outsets.
*/
- void calculateOutsets(boolean hasOutsets) {
- if (hasOutsets) {
+ void calculateOutsets() {
+ if (mHasOutsets) {
InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
} else {
mOutsets.setEmpty();
@@ -249,7 +250,8 @@
}
/**
- * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+ * Calculate the insets for the type
+ * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
*
* @param cutoutInsets The insets for the cutout.
*/
@@ -367,6 +369,28 @@
mLastContentInsets.set(-1, -1, -1, -1);
}
+ /**
+ * Sets whether the frame has outsets.
+ */
+ public void setHasOutsets(boolean hasOutsets) {
+ mHasOutsets = hasOutsets;
+ }
+
+ /**
+ * Sets whether the content has changed. This means that either the size or parent frame has
+ * changed.
+ */
+ public void setContentChanged(boolean contentChanged) {
+ mContentChanged = contentChanged;
+ }
+
+ /**
+ * @see #setContentChanged(boolean)
+ */
+ boolean hasContentChanged() {
+ return mContentChanged;
+ }
+
public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mParentFrame.writeToProto(proto, PARENT_FRAME);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 793ce60..5410676 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -19,6 +19,7 @@
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;
@@ -30,7 +31,6 @@
import android.view.MagnificationSpec;
import android.view.WindowInfo;
-import com.android.internal.view.IInputMethodClient;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
@@ -340,20 +340,6 @@
public abstract int getInputMethodWindowVisibleHeight(int displayId);
/**
- * Saves last input method window for transition.
- *
- * Note that it is assumed that this method is called only by InputMethodManagerService.
- */
- public abstract void saveLastInputMethodWindowForTransition();
-
- /**
- * Clears last input method window for transition.
- *
- * Note that it is assumed that this method is called only by InputMethodManagerService.
- */
- public abstract void clearLastInputMethodWindowForTransition();
-
- /**
* Notifies WindowManagerService that the current IME window status is being changed.
*
* <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
@@ -444,12 +430,24 @@
public abstract boolean isUidFocused(int uid);
/**
- * Returns {@code true} if a process that is identified by {@code client} has IME focus.
+ * Checks whether the specified process has IME focus or not.
+ *
+ * @param uid UID of the process to be queried
+ * @param pid PID of the process to be queried
+ * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
+ * focus
*/
- public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+ public abstract boolean isInputMethodClientFocus(int uid, int pid);
/**
* 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 ee128c7..942e47b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -182,6 +182,7 @@
import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
@@ -226,14 +227,13 @@
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -482,6 +482,10 @@
*/
WindowState[] mPendingRemoveTmp = new WindowState[20];
+ // TODO: use WindowProcessController once go/wm-unified is done.
+ /** Mapping of process pids to configurations */
+ final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>();
+
/**
* Windows whose surface should be destroyed.
*/
@@ -1411,7 +1415,6 @@
win.mToken.addWindow(win);
if (type == TYPE_INPUT_METHOD) {
- win.mGivenInsetsPending = true;
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
@@ -2206,7 +2209,7 @@
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = isDefaultDisplay;
win.mAnimatingExit = true;
- } else if (win.mWinAnimator.isAnimationSet()) {
+ } else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
win.mAnimatingExit = true;
@@ -2712,6 +2715,11 @@
}
}
+ @VisibleForTesting
+ void setRecentsAnimationController(RecentsAnimationController controller) {
+ mRecentsAnimationController = controller;
+ }
+
public RecentsAnimationController getRecentsAnimationController() {
return mRecentsAnimationController;
}
@@ -5037,12 +5045,8 @@
// -------------------------------------------------------------
@Override
- public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
- IInputContext inputContext) {
- if (client == null) throw new IllegalArgumentException("null client");
- if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- Session session = new Session(this, callback, client, inputContext);
- return session;
+ public IWindowSession openSession(IWindowSessionCallback callback) {
+ return new Session(this, callback);
}
@Override
@@ -5075,9 +5079,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5110,9 +5112,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5192,9 +5192,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5241,9 +5239,6 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser",
@@ -5272,9 +5267,6 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser",
@@ -5582,11 +5574,7 @@
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- final int prevImeAnimLayer =
- displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer;
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
- imWindowChanged |= prevImeAnimLayer
- != displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer;
}
}
@@ -5683,11 +5671,6 @@
mInputManagerCallback.freezeInputDispatchingLw();
- // Clear the last input window -- that is just used for
- // clean transitions between IMEs, and if we are freezing
- // the screen then the whole world is changing behind the scenes.
- mPolicy.setLastInputMethodWindowLw(null, null);
-
if (mAppTransition.isTransitionSet()) {
mAppTransition.freeze();
}
@@ -7300,23 +7283,6 @@
}
@Override
- public void saveLastInputMethodWindowForTransition() {
- synchronized (mWindowMap) {
- final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
- if (imeWindow != null) {
- mPolicy.setLastInputMethodWindowLw(imeWindow, mInputMethodTarget);
- }
- }
- }
-
- @Override
- public void clearLastInputMethodWindowForTransition() {
- synchronized (mWindowMap) {
- mPolicy.setLastInputMethodWindowLw(null, null);
- }
- }
-
- @Override
public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
@@ -7412,12 +7378,12 @@
}
@Override
- public boolean inputMethodClientHasFocus(IInputMethodClient client) {
+ public boolean isInputMethodClientFocus(int uid, int pid) {
synchronized (mWindowMap) {
// Check all displays if any input method window has focus.
for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
final DisplayContent displayContent = mRoot.mChildren.get(i);
- if (displayContent.inputMethodClientHasFocus(client)) {
+ if (displayContent.isInputMethodClientFocus(uid, pid)) {
return true;
}
}
@@ -7430,8 +7396,8 @@
// press home. Sometimes the IME won't go down.)
// Would be nice to fix this more correctly, but it's
// way at the end of a release, and this should be good enough.
- if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
- && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
+ if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
+ && mCurrentFocus.mSession.mPid == pid) {
return true;
}
}
@@ -7448,6 +7414,19 @@
return Display.INVALID_DISPLAY;
}
}
+
+ @Override
+ public void onProcessConfigurationChanged(int pid, Configuration newConfig) {
+ synchronized (mWindowMap) {
+ Configuration currentConfig = mProcessConfigurations.get(pid);
+ if (currentConfig == null) {
+ currentConfig = new Configuration(newConfig);
+ } else {
+ currentConfig.setTo(newConfig);
+ }
+ mProcessConfigurations.put(pid, currentConfig);
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 6e0ccfd..831418b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -20,7 +20,6 @@
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -28,10 +27,6 @@
import android.view.Display;
import android.view.IWindowManager;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -87,21 +82,48 @@
return -1;
}
+ private int getDisplayId(String opt) {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String option = "-d".equals(opt) ? opt : getNextOption();
+ if (option != null && "-d".equals(option)) {
+ try {
+ displayId = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e);
+ }
+ }
+ return displayId;
+ }
+
+ private void printInitialDisplaySize(PrintWriter pw , int displayId) {
+ final Point initialSize = new Point();
+ final Point baseSize = new Point();
+
+ try {
+ mInterface.getInitialDisplaySize(displayId, initialSize);
+ mInterface.getBaseDisplaySize(displayId, baseSize);
+ pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+ if (!initialSize.equals(baseSize)) {
+ pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+ }
+ } catch (RemoteException e) {
+ // Can't call getInitialDisplaySize() on IWindowManager or
+ // Can't call getBaseDisplaySize() on IWindowManager
+ pw.println("Remote exception: " + e);
+ }
+ }
+
private int runDisplaySize(PrintWriter pw) throws RemoteException {
String size = getNextArg();
int w, h;
+ final int displayId = getDisplayId(size);
if (size == null) {
- Point initialSize = new Point();
- Point baseSize = new Point();
- try {
- mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
- mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
- pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
- if (!initialSize.equals(baseSize)) {
- pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
- }
- } catch (RemoteException e) {
- }
+ printInitialDisplaySize(pw, displayId);
+ return 0;
+ } else if ("-d".equals(size)) {
+ printInitialDisplaySize(pw, displayId);
return 0;
} else if ("reset".equals(size)) {
w = h = -1;
@@ -114,8 +136,8 @@
String wstr = size.substring(0, div);
String hstr = size.substring(div+1);
try {
- w = parseDimension(wstr);
- h = parseDimension(hstr);
+ w = parseDimension(wstr, displayId);
+ h = parseDimension(hstr, displayId);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad number " + e);
return -1;
@@ -123,27 +145,38 @@
}
if (w >= 0 && h >= 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+ mInterface.setForcedDisplaySize(displayId, w, h);
} else {
- mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+ mInterface.clearForcedDisplaySize(displayId);
}
return 0;
}
+ private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
+ try {
+ final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
+ final int baseDensity = mInterface.getBaseDisplayDensity(displayId);
+ pw.println("Physical density: " + initialDensity);
+ if (initialDensity != baseDensity) {
+ pw.println("Override density: " + baseDensity);
+ }
+ } catch (RemoteException e) {
+ // Can't call getInitialDisplayDensity() on IWindowManager or
+ // Can't call getBaseDisplayDensity() on IWindowManager
+ pw.println("Remote exception: " + e);
+ }
+ }
+
private int runDisplayDensity(PrintWriter pw) throws RemoteException {
String densityStr = getNextArg();
int density;
+ final int displayId = getDisplayId(densityStr);
+
if (densityStr == null) {
- try {
- int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
- int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- pw.println("Physical density: " + initialDensity);
- if (initialDensity != baseDensity) {
- pw.println("Override density: " + baseDensity);
- }
- } catch (RemoteException e) {
- }
+ printInitialDisplayDensity(pw, displayId);
+ return 0;
+ } else if ("-d".equals(densityStr)) {
+ printInitialDisplayDensity(pw, displayId);
return 0;
} else if ("reset".equals(densityStr)) {
density = -1;
@@ -161,11 +194,10 @@
}
if (density > 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+ mInterface.setForcedDisplayDensityForUser(displayId, density,
UserHandle.USER_CURRENT);
} else {
- mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+ mInterface.clearForcedDisplayDensityForUser(displayId,
UserHandle.USER_CURRENT);
}
return 0;
@@ -174,6 +206,7 @@
private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
String overscanStr = getNextArgRequired();
Rect rect = new Rect();
+ final int displayId = getDisplayId(overscanStr);
if ("reset".equals(overscanStr)) {
rect.set(0, 0, 0, 0);
} else {
@@ -190,17 +223,16 @@
rect.bottom = Integer.parseInt(matcher.group(4));
}
- mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
- rect.bottom);
+ mInterface.setOverscan(displayId, rect.left, rect.top, rect.right, rect.bottom);
return 0;
}
private int runDisplayScaling(PrintWriter pw) throws RemoteException {
String scalingStr = getNextArgRequired();
if ("auto".equals(scalingStr)) {
- mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+ mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0);
} else if ("off".equals(scalingStr)) {
- mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+ mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1);
} else {
getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
return -1;
@@ -213,14 +245,14 @@
return 0;
}
- private int parseDimension(String s) throws NumberFormatException {
+ private int parseDimension(String s, int displayId) throws NumberFormatException {
if (s.endsWith("px")) {
return Integer.parseInt(s.substring(0, s.length() - 2));
}
if (s.endsWith("dp")) {
int density;
try {
- density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ density = mInterface.getBaseDisplayDensity(displayId);
} catch (RemoteException e) {
density = DisplayMetrics.DENSITY_DEFAULT;
}
@@ -236,14 +268,14 @@
pw.println("Window manager (window) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" size [reset|WxH|WdpxHdp]");
+ pw.println(" size [reset|WxH|WdpxHdp] [-d DISPLAY_ID]");
pw.println(" Return or override display size.");
pw.println(" width and height in pixels unless suffixed with 'dp'.");
- pw.println(" density [reset|DENSITY]");
+ pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]");
pw.println(" Return or override display density.");
- pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+ pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]");
pw.println(" Set overscan area for display.");
- pw.println(" scaling [off|auto]");
+ pw.println(" scaling [off|auto] [-d DISPLAY_ID]");
pw.println(" Set display scaling mode.");
pw.println(" dismiss-keyguard");
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5272b66..a4bac31 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -304,6 +304,8 @@
*/
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
+ private final Configuration mTempConfiguration = new Configuration();
+
/**
* The last content insets returned to the client in relayout. We use
* these in the bounds animation to ensure we only observe inset changes
@@ -359,8 +361,6 @@
*/
private final Rect mInsetFrame = new Rect();
- boolean mContentChanged;
-
// If a window showing a wallpaper: the requested offset for the
// wallpaper; if a wallpaper window: the currently applied offset.
float mWallpaperX = -1;
@@ -593,14 +593,14 @@
if (mForceSeamlesslyRotate || requested) {
mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
- mPendingSeamlessRotate.unrotate(transaction, this.mToken);
+ mPendingSeamlessRotate.unrotate(transaction, this);
mService.markForSeamlessRotation(this, true);
}
}
void finishSeamlessRotation() {
if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.finish(this.mToken, this);
+ mPendingSeamlessRotate.finish(this);
mFinishSeamlessRotateFrameNumber = getFrameNumber();
mPendingSeamlessRotate = null;
mService.markForSeamlessRotation(this, false);
@@ -787,7 +787,7 @@
}
@Override
- public void computeFrameLw(WindowFrames windowFrames) {
+ public void computeFrameLw() {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -796,8 +796,6 @@
return;
}
mHaveFrame = true;
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(
- windowFrames.parentFrameWasClippedByDisplayCutout());
final Task task = getTask();
final boolean inFullscreenContainer = inFullscreenContainer();
@@ -827,10 +825,9 @@
final int layoutYDiff;
if (inFullscreenContainer || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
- mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
- mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
- layoutDisplayFrame = windowFrames.mDisplayFrame;
- layoutContainingFrame = windowFrames.mParentFrame;
+ mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
+ layoutDisplayFrame = mWindowFrames.mDisplayFrame;
+ layoutContainingFrame = mWindowFrames.mParentFrame;
layoutXDiff = 0;
layoutYDiff = 0;
} else {
@@ -849,17 +846,17 @@
// 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
- > windowFrames.mContentFrame.bottom) {
+ > mWindowFrames.mContentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
- - windowFrames.mContentFrame.bottom;
+ - mWindowFrames.mContentFrame.bottom;
} else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
- > windowFrames.mParentFrame.bottom) {
+ > mWindowFrames.mParentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
// the pinned stack, they are handled separately in the PinnedStackController.
- mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
+ mWindowFrames.mContainingFrame.bottom = mWindowFrames.mParentFrame.bottom;
}
}
@@ -868,7 +865,7 @@
// if it wasn't set already. No need to intersect it with the (visible)
// "content frame" since it is allowed to be outside the visible desktop.
if (mWindowFrames.mContainingFrame.isEmpty()) {
- mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
+ mWindowFrames.mContainingFrame.set(mWindowFrames.mContentFrame);
}
}
@@ -878,10 +875,11 @@
// PIP edge case: When going from pinned to fullscreen, we apply a
// tempInsetFrame for the full task - but we're still at the start of the animation.
// To prevent a jump if there's a letterbox, restrict to the parent frame.
- mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
- mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
+ mInsetFrame.intersectUnchecked(mWindowFrames.mParentFrame);
+ mWindowFrames.mContainingFrame.intersectUnchecked(mWindowFrames.mParentFrame);
}
+ layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
layoutXDiff =
!mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
@@ -892,41 +890,24 @@
layoutContainingFrame =
!mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
- subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
- windowFrames.mDisplayFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
+ mTmpRect);
if (!layoutInParentFrame()) {
subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
- windowFrames.mParentFrame, mTmpRect);
- subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+ mWindowFrames.mParentFrame, mTmpRect);
+ subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame,
mTmpRect);
}
- layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutDisplayFrame.intersect(layoutContainingFrame);
}
final int pw = mWindowFrames.mContainingFrame.width();
final int ph = mWindowFrames.mContainingFrame.height();
- if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
- //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
- // + " to " + parentFrame);
- mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
- mContentChanged = true;
- }
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
mLastRequestedWidth = mRequestedWidth;
mLastRequestedHeight = mRequestedHeight;
- mContentChanged = true;
- }
-
- mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
- mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
- mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
- mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
- mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
- final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
- if (hasOutsets) {
- mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
+ mWindowFrames.setContentChanged(true);
}
final int fw = mWindowFrames.mFrame.width();
@@ -935,7 +916,7 @@
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
- mWindowFrames.calculateOutsets(hasOutsets);
+ mWindowFrames.calculateOutsets();
// Make sure the content and visible frames are inside of the
// final window frame.
@@ -997,7 +978,7 @@
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
+ final WmDisplayCutout c = mWindowFrames.mDisplayCutout.calculateRelativeTo(
mWindowFrames.mDisplayFrame);
mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
} else {
@@ -1006,7 +987,7 @@
}
mWindowFrames.setDisplayCutout(
- windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
+ mWindowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
// Offset the actual frame by the amount layout frame is off.
mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
@@ -1280,6 +1261,7 @@
@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) {
@@ -1382,7 +1364,7 @@
@Override
boolean hasContentToDisplay() {
if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE
- || (mWinAnimator.isAnimationSet() && !mService.mAppTransition.isTransitionSet()))) {
+ || (isAnimating() && !mService.mAppTransition.isTransitionSet()))) {
return true;
}
@@ -1461,9 +1443,9 @@
final AppWindowToken atoken = mAppToken;
if (atoken != null) {
return ((!isParentWindowHidden() && !atoken.hiddenRequested)
- || mWinAnimator.isAnimationSet());
+ || isAnimating());
}
- return !isParentWindowHidden() || mWinAnimator.isAnimationSet();
+ return !isParentWindowHidden() || isAnimating();
}
/**
@@ -1494,9 +1476,10 @@
if (mToken.waitingToShow && mService.mAppTransition.isTransitionSet()) {
return false;
}
+ final boolean parentAndClientVisible = !isParentWindowHidden()
+ && mViewVisibility == View.VISIBLE && !mToken.isHidden();
return mHasSurface && mPolicyVisibility && !mDestroying
- && ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.isHidden())
- || mWinAnimator.isAnimationSet());
+ && (parentAndClientVisible || isAnimating());
}
// TODO: Another visibility method that was added late in the release to minimize risk.
@@ -1526,7 +1509,7 @@
final AppWindowToken atoken = mAppToken;
return isDrawnLw() && mPolicyVisibility
&& ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
- || mWinAnimator.isAnimationSet());
+ || isAnimating());
}
/**
@@ -1580,7 +1563,7 @@
// to determine if it's occluding apps.
return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
|| (mIsWallpaper && mWallpaperVisible))
- && isDrawnLw() && !mWinAnimator.isAnimationSet();
+ && isDrawnLw() && !isAnimating();
}
@Override
@@ -1733,7 +1716,7 @@
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
*/
private boolean hasMoved() {
- return mHasSurface && (mContentChanged || mMovedByResize)
+ return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
&& !mAnimatingExit
&& (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
|| mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
@@ -1867,7 +1850,7 @@
+ " mRemoveOnExit=" + mRemoveOnExit
+ " mHasSurface=" + mHasSurface
+ " surfaceShowing=" + mWinAnimator.getShown()
- + " isAnimationSet=" + mWinAnimator.isAnimationSet()
+ + " animating=" + isAnimating()
+ " app-animation="
+ (mAppToken != null ? mAppToken.isSelfAnimating() : "false")
+ " mWillReplaceWindow=" + mWillReplaceWindow
@@ -1934,7 +1917,7 @@
mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
- final boolean isAnimating = mWinAnimator.isAnimationSet()
+ final boolean isAnimating = isAnimating()
&& (mAppToken == null || !mAppToken.isWaitingForTransitionStart());
final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
&& mAppToken.isLastWindow(this);
@@ -2270,8 +2253,17 @@
}
}
+ private Configuration getProcessGlobalConfiguration() {
+ // 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 = isChildWindow() ? getParentWindow().mSession.mPid : mSession.mPid;
+ mTempConfiguration.setTo(mService.mProcessConfigurations.get(
+ pid, mService.mRoot.getConfiguration()));
+ return mTempConfiguration;
+ }
+
void getMergedConfiguration(MergedConfiguration outConfiguration) {
- final Configuration globalConfig = mService.mRoot.getConfiguration();
+ final Configuration globalConfig = getProcessGlobalConfiguration();
final Configuration overrideConfig = getMergedOverrideConfiguration();
outConfiguration.setConfiguration(globalConfig, overrideConfig);
}
@@ -2443,10 +2435,10 @@
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
- + mPolicyVisibility + " isAnimationSet=" + mWinAnimator.isAnimationSet());
+ + mPolicyVisibility + " animating=" + isAnimating());
if (!mToken.okToAnimate()) {
doAnimation = false;
- } else if (mPolicyVisibility && !mWinAnimator.isAnimationSet()) {
+ } else if (mPolicyVisibility && !isAnimating()) {
// Check for the case where we are currently visible and
// not animating; we do not want to do animation at such a
// point to become visible when we already are.
@@ -2485,7 +2477,7 @@
}
if (doAnimation) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
- if (!mWinAnimator.isAnimationSet()) {
+ if (!isAnimating()) {
doAnimation = false;
}
}
@@ -2874,7 +2866,11 @@
return mAppToken.mFrozenMergedConfig.peek();
}
- return super.getConfiguration();
+ // We use the process config this window is associated with as the based global config since
+ // the process can override it config, but isn't part of the window hierarchy.
+ final Configuration config = getProcessGlobalConfiguration();
+ config.updateFrom(getMergedOverrideConfiguration());
+ return config;
}
void reportResized() {
@@ -3221,10 +3217,8 @@
+ " mWallpaperVisible=" + mWallpaperVisible);
}
if (dumpAll) {
- pw.println(prefix + "mBaseLayer=" + mBaseLayer
- + " mSubLayer=" + mSubLayer
- + " mAnimLayer=" + mLayer + "=" + mWinAnimator.mAnimLayer
- + " mLastLayer=" + mWinAnimator.mLastLayer);
+ pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
+ pw.print(" mSubLayer="); pw.print(mSubLayer);
}
if (dumpAll) {
pw.println(prefix + "mToken=" + mToken);
@@ -3702,7 +3696,7 @@
+ " tok.hiddenRequested="
+ (mAppToken != null && mAppToken.hiddenRequested)
+ " tok.hidden=" + (mAppToken != null && mAppToken.isHidden())
- + " animationSet=" + mWinAnimator.isAnimationSet()
+ + " animating=" + isAnimating()
+ " tok animating="
+ (mAppToken != null && mAppToken.isSelfAnimating())
+ " Callers=" + Debug.getCallers(4));
@@ -3754,18 +3748,6 @@
return windowInfo;
}
- int getHighestAnimLayer() {
- int highest = mWinAnimator.mAnimLayer;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState c = mChildren.get(i);
- final int childLayer = c.getHighestAnimLayer();
- if (childLayer > highest) {
- highest = childLayer;
- }
- }
- return highest;
- }
-
@Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
if (mChildren.isEmpty()) {
@@ -4115,25 +4097,25 @@
}
if (DEBUG_VISIBILITY) {
Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw()
- + ", isAnimationSet=" + mWinAnimator.isAnimationSet());
+ + ", animating=" + isAnimating());
if (!isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
+ " pv=" + mPolicyVisibility
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
+ " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
- + " a=" + mWinAnimator.isAnimationSet());
+ + " a=" + isAnimating());
}
}
results.numInteresting++;
if (isDrawnLw()) {
results.numDrawn++;
- if (!mWinAnimator.isAnimationSet()) {
+ if (!isAnimating()) {
results.numVisible++;
}
results.nowGone = false;
- } else if (mWinAnimator.isAnimationSet()) {
+ } else if (isAnimating()) {
results.nowGone = false;
}
}
@@ -4540,7 +4522,8 @@
updateSurfacePosition(getPendingTransaction());
}
- private void updateSurfacePosition(Transaction t) {
+ @VisibleForTesting
+ void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
return;
}
@@ -4762,6 +4745,15 @@
return mWindowFrames.mVisibleInsets;
}
+ @Override
+ public WindowFrames getWindowFrames() {
+ return mWindowFrames;
+ }
+
+ void resetContentChanged() {
+ mWindowFrames.setContentChanged(false);
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 979149a..2beb788 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -107,8 +107,6 @@
private final WallpaperController mWallpaperControllerLocked;
boolean mAnimationIsEntrance;
- int mAnimLayer;
- int mLastLayer;
/**
* Set when we have changed the size of the surface, to know that
@@ -135,7 +133,6 @@
float mLastAlpha = 0;
Rect mTmpClipRect = new Rect();
- Rect mTmpFinalClipRect = new Rect();
Rect mLastClipRect = new Rect();
Rect mLastFinalClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
@@ -162,8 +159,6 @@
* window is first added or shown, cleared when the callback has been made. */
boolean mEnteringAnimation;
- private boolean mAnimationStartDelayed;
-
private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
/** The pixel format of the underlying SurfaceControl */
@@ -253,13 +248,6 @@
mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
}
- /**
- * Is the window or its container currently set to animate or currently animating?
- */
- boolean isAnimationSet() {
- return mWin.isAnimating();
- }
-
void cancelExitAnimationForNextAnimationLocked() {
if (DEBUG_ANIM) Slog.d(TAG,
"cancelExitAnimationForNextAnimationLocked: " + mWin);
@@ -275,10 +263,6 @@
+ ", reportedVisible="
+ (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
- if (mAnimator.mWindowDetachedWallpaper == mWin) {
- mAnimator.mWindowDetachedWallpaper = null;
- }
-
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
@@ -288,7 +272,6 @@
displayContent.setLayoutNeeded();
}
}
-
mWin.onExitAnimationDone();
final int displayId = mWin.getDisplayId();
int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM;
@@ -539,14 +522,13 @@
}
if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController
- + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top
- + ", animLayer=" + mAnimLayer);
+ + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
WindowManagerService.logSurface(w, "CREATE pos=("
+ w.getFrameLw().left + "," + w.getFrameLw().top + ") ("
- + width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false);
+ + width + "x" + height + ")" + " HIDE", false);
}
mLastHidden = true;
@@ -1133,8 +1115,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation change skips hidden " + w);
}
- } else if (mLastLayer != mAnimLayer
- || mLastAlpha != mShownAlpha
+ } else if (mLastAlpha != mShownAlpha
|| mLastDsDx != mDsDx
|| mLastDtDx != mDtDx
|| mLastDsDy != mDsDy
@@ -1144,7 +1125,6 @@
|| mLastHidden) {
displayed = true;
mLastAlpha = mShownAlpha;
- mLastLayer = mAnimLayer;
mLastDsDx = mDsDx;
mLastDtDx = mDtDx;
mLastDsDy = mDsDy;
@@ -1153,7 +1133,7 @@
w.mLastVScale = w.mVScale;
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"controller=" + mSurfaceController +
- "alpha=" + mShownAlpha + " layer=" + mAnimLayer
+ "alpha=" + mShownAlpha
+ " matrix=[" + mDsDx + "*" + w.mHScale
+ "," + mDtDx + "*" + w.mVScale
+ "][" + mDtDy + "*" + w.mHScale
@@ -1197,7 +1177,7 @@
w.mToken.hasVisible = true;
}
} else {
- if (DEBUG_ANIM && isAnimationSet()) {
+ if (DEBUG_ANIM && mWin.isAnimating()) {
Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
displayed = true;
@@ -1407,7 +1387,7 @@
}
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- return isAnimationSet();
+ return mWin.isAnimating();
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -1460,9 +1440,6 @@
pw.print(" mDtDy="); pw.print(mDtDy);
pw.print(" mDsDy="); pw.println(mDsDy);
}
- if (mAnimationStartDelayed) {
- pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed);
- }
}
@Override
@@ -1520,10 +1497,6 @@
mChildrenDetached = true;
}
- int getLayer() {
- return mLastLayer;
- }
-
void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) {
mOffsetPositionForStackResize = offsetPositionForStackResize;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d6f9ac3..080a3a2 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -92,19 +92,12 @@
static final int SET_UPDATE_ROTATION = 1 << 0;
static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
- static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
- static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 4;
+ static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2;
+ static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3;
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
- private static final class LayerAndToken {
- public int layer;
- public AppWindowToken token;
- }
- private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
-
private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
private final Runnable mPerformSurfacePlacement;
@@ -299,10 +292,16 @@
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mService.mOpeningApps,
mService.mClosingApps);
- final AppWindowToken animLpToken = mService.mPolicy.allowAppAnimationsLw()
+ final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw();
+ final AppWindowToken animLpToken = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
: null;
-
+ final AppWindowToken topOpeningApp = allowAnimations
+ ? getTopApp(mService.mOpeningApps, false /* ignoreHidden */)
+ : null;
+ final AppWindowToken topClosingApp = allowAnimations
+ ? getTopApp(mService.mClosingApps, false /* ignoreHidden */)
+ : null;
final LayoutParams animLp = getAnimLp(animLpToken);
overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
@@ -314,17 +313,14 @@
try {
processApplicationsAnimatingInPlace(transit);
- mTmpLayerAndToken.token = null;
- handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
- final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
- final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp,
- voiceInteraction);
+ handleClosingApps(transit, animLp, voiceInteraction);
+ handleOpeningApps(transit, animLp, voiceInteraction);
mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
final int flags = mService.mAppTransition.getTransitFlags();
- layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp,
- topClosingApp, mService.mOpeningApps, mService.mClosingApps);
+ layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, topClosingApp,
+ mService.mOpeningApps, mService.mClosingApps);
handleNonAppWindowsInTransition(transit, flags);
mService.mAppTransition.postAnimationCallback();
mService.mAppTransition.clear();
@@ -451,10 +447,7 @@
return false;
}
- private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
- boolean voiceInteraction) {
- AppWindowToken topOpeningApp = null;
- int topOpeningLayer = Integer.MIN_VALUE;
+ private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
final int appsCount = mService.mOpeningApps.size();
for (int i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -479,24 +472,15 @@
"<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
}
- if (animLp != null) {
- final int layer = wtoken.getHighestAnimLayer();
- if (topOpeningApp == null || layer > topOpeningLayer) {
- topOpeningApp = wtoken;
- topOpeningLayer = layer;
- }
- }
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
wtoken.attachThumbnailAnimation();
} else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
wtoken.attachCrossProfileAppsThumbnailAnimation();
}
}
- return topOpeningApp;
}
- private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction,
- LayerAndToken layerAndToken) {
+ private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
final int appsCount;
appsCount = mService.mClosingApps.size();
for (int i = 0; i < appsCount; i++) {
@@ -519,13 +503,6 @@
wtoken.getController().removeStartingWindow();
}
- if (animLp != null) {
- int layer = wtoken.getHighestAnimLayer();
- if (layerAndToken.token == null || layer > layerAndToken.layer) {
- layerAndToken.token = wtoken;
- layerAndToken.layer = layer;
- }
- }
if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
wtoken.attachThumbnailAnimation();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e411c0a..fefd305 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -162,7 +163,7 @@
for (int i = 0; i < count; i++) {
final WindowState win = mChildren.get(i);
- if (win.mWinAnimator.isAnimationSet()) {
+ if (win.isAnimating()) {
delayed = true;
}
changed |= win.onSetAppExiting();
@@ -234,18 +235,6 @@
return false;
}
- int getHighestAnimLayer() {
- int highest = -1;
- for (int j = 0; j < mChildren.size(); j++) {
- final WindowState w = mChildren.get(j);
- final int wLayer = w.getHighestAnimLayer();
- if (wLayer > highest) {
- highest = wLayer;
- }
- }
- return highest;
- }
-
AppWindowToken asAppWindowToken() {
// TODO: Not sure if this is the best way to handle this vs. using instanceof and casting.
// I am not an app window token!
@@ -275,6 +264,7 @@
// to another display before the window behind
// it is ready.
+ updateSurfaceSize(dc);
super.onDisplayChanged(dc);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index becde73..157b634 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -37,6 +37,7 @@
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
"com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
+ "com_android_server_security_VerityUtils.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_storage_AppFuseBridge.cpp",
"com_android_server_SystemServer.cpp",
@@ -89,7 +90,6 @@
"libsensorservicehidl",
"libgui",
"libusbhost",
- "libsuspend",
"libtinyalsa",
"libEGL",
"libGLESv2",
@@ -121,6 +121,7 @@
"android.hardware.vr@1.0",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
+ "android.system.suspend@1.0",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 02ad6c7..0ff60e4 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -30,6 +30,8 @@
#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
@@ -39,7 +41,6 @@
#include <log/log.h>
#include <utils/misc.h>
#include <utils/Log.h>
-#include <suspend/autosuspend.h>
using android::hardware::Return;
using android::hardware::Void;
@@ -49,6 +50,8 @@
using android::hardware::power::V1_1::PowerStateSubsystem;
using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::hardware::hidl_vec;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::ISystemSuspendCallback;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -63,6 +66,7 @@
extern sp<IPowerV1_0> getPowerHalV1_0();
extern sp<IPowerV1_1> getPowerHalV1_1();
extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
+extern sp<ISystemSuspend> getSuspendHal();
// Java methods used in getLowPowerStats
static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -70,16 +74,19 @@
static jmethodID jputVoter = NULL;
static jmethodID jputState = NULL;
-static void wakeup_callback(bool success)
-{
- ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
- int ret = sem_post(&wakeup_sem);
- if (ret < 0) {
- char buf[80];
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error posting wakeup sem: %s\n", buf);
+class WakeupCallback : public ISystemSuspendCallback {
+public:
+ Return<void> notifyWakeup(bool success) override {
+ ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+ int ret = sem_post(&wakeup_sem);
+ if (ret < 0) {
+ char buf[80];
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error posting wakeup sem: %s\n", buf);
+ }
+ return Void();
}
-}
+};
static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
{
@@ -101,11 +108,14 @@
return -1;
}
ALOGV("Registering callback...");
- autosuspend_set_wakeup_callback(&wakeup_callback);
+ sp<ISystemSuspend> suspendHal = getSuspendHal();
+ suspendHal->registerCallback(new WakeupCallback());
}
// Wait for wakeup.
ALOGV("Waiting for wakeup...");
+ // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup
+ // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events.
int ret = sem_wait(&wakeup_sem);
if (ret < 0) {
char buf[80];
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index b2d35d4..0c9b5f4 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -19,6 +19,7 @@
//#define LOG_NDEBUG 0
#include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
@@ -35,7 +36,7 @@
#include <utils/Log.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
-#include <suspend/autosuspend.h>
+#include <hidl/ServiceManagement.h>
#include "com_android_server_power_PowerManagerService.h"
@@ -44,6 +45,9 @@
using android::hardware::power::V1_0::PowerHint;
using android::hardware::power::V1_0::Feature;
using android::String8;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::IWakeLock;
+using android::system::suspend::V1_0::WakeLockType;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -171,6 +175,46 @@
}
}
+static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<IWakeLock> gSuspendBlocker = nullptr;
+static std::mutex gSuspendMutex;
+
+// Assume SystemSuspend HAL is always alive.
+// TODO: Force device to restart if SystemSuspend HAL dies.
+sp<ISystemSuspend> getSuspendHal() {
+ static std::once_flag suspendHalFlag;
+ std::call_once(suspendHalFlag, [](){
+ ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default");
+ gSuspendHal = ISystemSuspend::getService();
+ assert(gSuspendHal != nullptr);
+ });
+ return gSuspendHal;
+}
+
+void enableAutoSuspend() {
+ static bool enabled = false;
+
+ std::lock_guard<std::mutex> lock(gSuspendMutex);
+ if (!enabled) {
+ sp<ISystemSuspend> suspendHal = getSuspendHal();
+ suspendHal->enableAutosuspend();
+ enabled = true;
+ }
+ if (gSuspendBlocker) {
+ gSuspendBlocker->release();
+ gSuspendBlocker.clear();
+ }
+}
+
+void disableAutoSuspend() {
+ std::lock_guard<std::mutex> lock(gSuspendMutex);
+ if (!gSuspendBlocker) {
+ sp<ISystemSuspend> suspendHal = getSuspendHal();
+ gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
+ "PowerManager.SuspendLockout");
+ }
+}
+
// ----------------------------------------------------------------------------
static void nativeInit(JNIEnv* env, jobject obj) {
@@ -207,13 +251,13 @@
static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
if (enable) {
android::base::Timer t;
- autosuspend_enable();
+ enableAutoSuspend();
if (t.duration() > 100ms) {
ALOGD("Excessive delay in autosuspend_enable() while turning screen off");
}
} else {
android::base::Timer t;
- autosuspend_disable();
+ disableAutoSuspend();
if (t.duration() > 100ms) {
ALOGD("Excessive delay in autosuspend_disable() while turning screen on");
}
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
new file mode 100644
index 0000000..d0f173b
--- /dev/null
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -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.
+ */
+
+#define LOG_TAG "VerityUtils"
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include <utils/Log.h>
+
+#include <string.h>
+
+// TODO(112037636): Always include once fsverity.h is upstreamed and backported.
+#define HAS_FSVERITY 0
+
+#if HAS_FSVERITY
+#include <linux/fsverity.h>
+#endif
+
+namespace android {
+
+namespace {
+
+class JavaByteArrayHolder {
+ public:
+ static JavaByteArrayHolder* newArray(JNIEnv* env, jsize size) {
+ return new JavaByteArrayHolder(env, size);
+ }
+
+ jbyte* getRaw() {
+ return mElements;
+ }
+
+ jbyteArray release() {
+ mEnv->ReleaseByteArrayElements(mBytes, mElements, 0);
+ mElements = nullptr;
+ return mBytes;
+ }
+
+ private:
+ JavaByteArrayHolder(JNIEnv* env, jsize size) {
+ mEnv = env;
+ mBytes = mEnv->NewByteArray(size);
+ mElements = mEnv->GetByteArrayElements(mBytes, nullptr);
+ memset(mElements, 0, size);
+ }
+
+ virtual ~JavaByteArrayHolder() {
+ LOG_ALWAYS_FATAL_IF(mElements == nullptr, "Elements are not released");
+ }
+
+ JNIEnv* mEnv;
+ jbyteArray mBytes;
+ jbyte* mElements;
+};
+
+jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) {
+#if HAS_FSVERITY
+ auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor));
+ fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii->getRaw());
+
+ memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic));
+ desc->major_version = 1;
+ desc->minor_version = 0;
+ desc->log_data_blocksize = 12;
+ desc->log_tree_blocksize = 12;
+ desc->data_algorithm = FS_VERITY_ALG_SHA256;
+ desc->tree_algorithm = FS_VERITY_ALG_SHA256;
+ desc->flags = 0;
+ desc->orig_file_size = fileSize;
+ desc->auth_ext_count = 1;
+
+ return raii->release();
+#else
+ LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
+ return 0;
+#endif // HAS_FSVERITY
+}
+
+jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId,
+ jint extensionDataSize) {
+#if HAS_FSVERITY
+ auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension));
+ fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii->getRaw());
+
+ ext->length = sizeof(fsverity_extension) + extensionDataSize;
+ ext->type = extensionId;
+
+ return raii->release();
+#else
+ LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
+ return 0;
+#endif // HAS_FSVERITY
+}
+
+jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */,
+ jint offsetToDescriptorHead) {
+#if HAS_FSVERITY
+ auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer));
+ fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii->getRaw());
+
+ footer->desc_reverse_offset = offsetToDescriptorHead + sizeof(fsverity_footer);
+ memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic));
+
+ return raii->release();
+#else
+ LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
+ return 0;
+#endif // HAS_FSVERITY
+}
+
+const JNINativeMethod sMethods[] = {
+ { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
+ { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },
+ { "constructFsverityFooterNative", "(I)[B", (void *)constructFsverityFooter },
+};
+
+} // namespace
+
+int register_android_server_security_VerityUtils(JNIEnv* env) {
+ return jniRegisterNativeMethods(env,
+ "com/android/server/security/VerityUtils", sMethods, NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 93c4829..6c2a894 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -390,7 +390,11 @@
[&result, &sidebandStream](Result res, const native_handle_t* handle) {
result = res;
if (res == Result::OK) {
- sidebandStream = handle;
+ if (handle) {
+ sidebandStream = native_handle_clone(handle);
+ } else {
+ result = Result::UNKNOWN;
+ }
}
});
if (result != Result::OK) {
@@ -398,7 +402,7 @@
result);
return UNKNOWN_ERROR;
}
- connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, false);
+ connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, true);
}
connection.mSurface = surface;
if (connection.mSurface != nullptr) {
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index bb6e684..918f57e 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -54,6 +54,7 @@
int register_android_server_GraphicsStatsService(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
+int register_android_server_security_VerityUtils(JNIEnv* env);
};
using namespace android;
@@ -101,5 +102,6 @@
register_android_server_GraphicsStatsService(env);
register_android_hardware_display_DisplayViewport(env);
register_android_server_net_NetworkStatsService(env);
+ register_android_server_security_VerityUtils(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 0c0ce8d..a8b9b0c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -18,27 +18,23 @@
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.DeviceAdminService;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDeviceAdminService;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.server.am.PersistentConnection;
+import com.android.server.appbinding.AppBindingUtils;
import java.io.PrintWriter;
-import java.util.List;
/**
* Manages connections to persistent services in owner packages.
@@ -74,6 +70,11 @@
}
@Override
+ protected int getBindFlags() {
+ return Context.BIND_FOREGROUND_SERVICE;
+ }
+
+ @Override
protected IDeviceAdminService asInterface(IBinder binder) {
return IDeviceAdminService.Stub.asInterface(binder);
}
@@ -100,40 +101,14 @@
*/
@Nullable
private ServiceInfo findService(@NonNull String packageName, int userId) {
- final Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE);
- intent.setPackage(packageName);
-
- try {
- final ParceledListSlice<ResolveInfo> pls = mInjector.getIPackageManager()
- .queryIntentServices(intent, null, /* flags=*/ 0, userId);
- if (pls == null) {
- return null;
- }
- final List<ResolveInfo> list = pls.getList();
- if (list.size() == 0) {
- return null;
- }
- // Note if multiple services are found, that's an error, even if only one of them
- // is exported.
- if (list.size() > 1) {
- Log.e(TAG, "More than one DeviceAdminService's found in package "
- + packageName
- + ". They'll all be ignored.");
- return null;
- }
- final ServiceInfo si = list.get(0).serviceInfo;
-
- if (!permission.BIND_DEVICE_ADMIN.equals(si.permission)) {
- Log.e(TAG, "DeviceAdminService "
- + si.getComponentName().flattenToShortString()
- + " must be protected with " + permission.BIND_DEVICE_ADMIN
- + ".");
- return null;
- }
- return si;
- } catch (RemoteException e) {
- }
- return null;
+ return AppBindingUtils.findService(
+ packageName,
+ userId,
+ DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE,
+ permission.BIND_DEVICE_ADMIN,
+ DeviceAdminService.class,
+ mInjector.getIPackageManager(),
+ new StringBuilder() /* ignore error message */);
}
/**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4c71d65..eeb4ad3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6019,7 +6019,7 @@
success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
if (!success) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
- } else if (isManagedProfile(userId)) {
+ } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) {
sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
@@ -6034,7 +6034,6 @@
if (!mHasFeature) {
return;
}
- Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
@@ -10107,13 +10106,15 @@
if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) {
if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) {
throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS
- + " is deprecated. Please use the user restriction "
- + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " instead.");
+ + " is deprecated. Please use one of the user restrictions "
+ + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " or "
+ + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY + " instead.");
}
if (!mUserManager.isManagedProfile(callingUserId)) {
Slog.e(LOG_TAG, "Ignoring setSecureSetting request for "
+ setting + ". User restriction "
- + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
+ + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " or "
+ + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+ " should be used instead.");
} else {
try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9d63305..b8241d0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -63,11 +63,11 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.widget.ILockSettings;
-import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.ActivityTaskManagerService;
+import com.android.server.appbinding.AppBindingService;
import com.android.server.audio.AudioService;
-import com.android.server.biometrics.BiometricPromptService;
+import com.android.server.biometrics.BiometricService;
import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.camera.CameraServiceProxy;
import com.android.server.clipboard.ClipboardService;
@@ -1590,7 +1590,7 @@
if (hasFeatureFace || hasFeatureFingerprint) {
// Start this service after all biometric services.
traceBeginAndSlog("StartBiometricPromptService");
- mSystemServiceManager.startService(BiometricPromptService.class);
+ mSystemServiceManager.startService(BiometricService.class);
traceEnd();
}
@@ -1711,6 +1711,10 @@
traceEnd();
}
+ traceBeginAndSlog("AppServiceManager");
+ mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
+ traceEnd();
+
// It is now time to start up the app processes...
traceBeginAndSlog("MakeVibratorServiceReady");
diff --git a/services/net/java/android/net/netlink/InetDiagMessage.java b/services/net/java/android/net/netlink/InetDiagMessage.java
new file mode 100644
index 0000000..af9e601
--- /dev/null
+++ b/services/net/java/android/net/netlink/InetDiagMessage.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import static android.os.Process.INVALID_UID;
+import static android.net.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
+import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.NETLINK_INET_DIAG;
+
+import android.os.Build;
+import android.os.Process;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.DatagramSocket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * A NetlinkMessage subclass for netlink inet_diag messages.
+ *
+ * see also: <linux_src>/include/uapi/linux/inet_diag.h
+ *
+ * @hide
+ */
+public class InetDiagMessage extends NetlinkMessage {
+ public static final String TAG = "InetDiagMessage";
+ private static final int TIMEOUT_MS = 500;
+
+ public static byte[] InetDiagReqV2(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, int family, short flags) {
+ final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr();
+ nlMsgHdr.nlmsg_len = bytes.length;
+ nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+ nlMsgHdr.nlmsg_flags = flags;
+ nlMsgHdr.pack(byteBuffer);
+
+ final StructInetDiagReqV2 inetDiagReqV2 = new StructInetDiagReqV2(protocol, local, remote,
+ family);
+ inetDiagReqV2.pack(byteBuffer);
+ return bytes;
+ }
+
+ public StructInetDiagMsg mStructInetDiagMsg;
+
+ private InetDiagMessage(StructNlMsgHdr header) {
+ super(header);
+ mStructInetDiagMsg = new StructInetDiagMsg();
+ }
+
+ public static InetDiagMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
+ final InetDiagMessage msg = new InetDiagMessage(header);
+ msg.mStructInetDiagMsg = StructInetDiagMsg.parse(byteBuffer);
+ return msg;
+ }
+
+ private static int lookupUidByFamily(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, int family, short flags,
+ FileDescriptor fd)
+ throws ErrnoException, InterruptedIOException {
+ byte[] msg = InetDiagReqV2(protocol, local, remote, family, flags);
+ NetlinkSocket.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS);
+ ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS);
+
+ final NetlinkMessage nlMsg = NetlinkMessage.parse(response);
+ final StructNlMsgHdr hdr = nlMsg.getHeader();
+ if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
+ return INVALID_UID;
+ }
+ if (nlMsg instanceof InetDiagMessage) {
+ return ((InetDiagMessage) nlMsg).mStructInetDiagMsg.idiag_uid;
+ }
+ return INVALID_UID;
+ }
+
+ private static final int FAMILY[] = {AF_INET6, AF_INET};
+
+ private static int lookupUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, FileDescriptor fd)
+ throws ErrnoException, InterruptedIOException {
+ int uid;
+
+ for (int family : FAMILY) {
+ /**
+ * For exact match lookup, swap local and remote for UDP lookups due to kernel
+ * bug which will not be fixed. See aosp/755889 and
+ * https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html
+ */
+ if (protocol == IPPROTO_UDP) {
+ uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd);
+ } else {
+ uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd);
+ }
+ if (uid != INVALID_UID) {
+ return uid;
+ }
+ }
+
+ /**
+ * For UDP it's possible for a socket to send packets to arbitrary destinations, even if the
+ * socket is not connected (and even if the socket is connected to a different destination).
+ * If we want this API to work for such packets, then on miss we need to do a second lookup
+ * with only the local address and port filled in.
+ * Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard.
+ */
+ if (protocol == IPPROTO_UDP) {
+ try {
+ InetSocketAddress wildcard = new InetSocketAddress(
+ Inet6Address.getByName("::"), 0);
+ uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6,
+ (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
+ if (uid != INVALID_UID) {
+ return uid;
+ }
+ wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0);
+ uid = lookupUidByFamily(protocol, local, wildcard, AF_INET,
+ (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
+ if (uid != INVALID_UID) {
+ return uid;
+ }
+ } catch (UnknownHostException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ return INVALID_UID;
+ }
+
+ /**
+ * Use an inet_diag socket to look up the UID associated with the input local and remote
+ * address/port and protocol of a connection.
+ */
+ public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote) {
+ try {
+ final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
+ NetlinkSocket.connectToKernel(fd);
+
+ return lookupUid(protocol, local, remote, fd);
+
+ } catch (ErrnoException | SocketException | IllegalArgumentException
+ | InterruptedIOException e) {
+ Log.e(TAG, e.toString());
+ }
+ return INVALID_UID;
+ }
+
+ @Override
+ public String toString() {
+ return "InetDiagMessage{ "
+ + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
+ + "inet_diag_msg{"
+ + (mStructInetDiagMsg == null ? "" : mStructInetDiagMsg.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/services/net/java/android/net/netlink/NetlinkConstants.java b/services/net/java/android/net/netlink/NetlinkConstants.java
index e331701..fc1551c 100644
--- a/services/net/java/android/net/netlink/NetlinkConstants.java
+++ b/services/net/java/android/net/netlink/NetlinkConstants.java
@@ -54,6 +54,12 @@
return String.valueOf(family);
}
+ public static String stringForProtocol(int protocol) {
+ if (protocol == OsConstants.IPPROTO_TCP) { return "IPPROTO_TCP"; }
+ if (protocol == OsConstants.IPPROTO_UDP) { return "IPPROTO_UDP"; }
+ return String.valueOf(protocol);
+ }
+
public static String hexify(byte[] bytes) {
if (bytes == null) { return "(null)"; }
return HexDump.toHexString(bytes);
@@ -90,6 +96,9 @@
public static final short RTM_GETRULE = 34;
public static final short RTM_NEWNDUSEROPT = 68;
+ /* see <linux_src>/include/uapi/linux/sock_diag.h */
+ public static final short SOCK_DIAG_BY_FAMILY = 20;
+
public static String stringForNlMsgType(short nlm_type) {
switch (nlm_type) {
case NLMSG_NOOP: return "NLMSG_NOOP";
diff --git a/services/net/java/android/net/netlink/NetlinkMessage.java b/services/net/java/android/net/netlink/NetlinkMessage.java
index 3bf75ca..a325db8 100644
--- a/services/net/java/android/net/netlink/NetlinkMessage.java
+++ b/services/net/java/android/net/netlink/NetlinkMessage.java
@@ -69,6 +69,8 @@
case NetlinkConstants.RTM_DELNEIGH:
case NetlinkConstants.RTM_GETNEIGH:
return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
+ case NetlinkConstants.SOCK_DIAG_BY_FAMILY:
+ return (NetlinkMessage) InetDiagMessage.parse(nlmsghdr, byteBuffer);
default:
if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
// Netlink control message. Just parse the header for now,
diff --git a/services/net/java/android/net/netlink/StructInetDiagMsg.java b/services/net/java/android/net/netlink/StructInetDiagMsg.java
new file mode 100644
index 0000000..da824ad
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructInetDiagMsg.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import android.util.Log;
+
+/**
+ * struct inet_diag_msg
+ *
+ * see <linux_src>/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_msg {
+ * __u8 idiag_family;
+ * __u8 idiag_state;
+ * __u8 idiag_timer;
+ * __u8 idiag_retrans;
+ * struct inet_diag_sockid id;
+ * __u32 idiag_expires;
+ * __u32 idiag_rqueue;
+ * __u32 idiag_wqueue;
+ * __u32 idiag_uid;
+ * __u32 idiag_inode;
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagMsg {
+ public static final int STRUCT_SIZE = 4 + StructInetDiagSockId.STRUCT_SIZE + 20;
+ private static final int IDIAG_UID_OFFSET = StructNlMsgHdr.STRUCT_SIZE + 4 +
+ StructInetDiagSockId.STRUCT_SIZE + 12;
+ public int idiag_uid;
+
+ public static StructInetDiagMsg parse(ByteBuffer byteBuffer) {
+ StructInetDiagMsg struct = new StructInetDiagMsg();
+ struct.idiag_uid = byteBuffer.getInt(IDIAG_UID_OFFSET);
+ return struct;
+ }
+
+ @Override
+ public String toString() {
+ return "StructInetDiagMsg{ "
+ + "idiag_uid{" + idiag_uid + "}, "
+ + "}";
+ }
+}
diff --git a/services/net/java/android/net/netlink/StructInetDiagReqV2.java b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
new file mode 100644
index 0000000..49a9325
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructInetDiagReqV2.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 android.net.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * struct inet_diag_req_v2
+ *
+ * see <linux_src>/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_req_v2 {
+ * __u8 sdiag_family;
+ * __u8 sdiag_protocol;
+ * __u8 idiag_ext;
+ * __u8 pad;
+ * __u32 idiag_states;
+ * struct inet_diag_sockid id;
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagReqV2 {
+ public static final int STRUCT_SIZE = 8 + StructInetDiagSockId.STRUCT_SIZE;
+
+ private final byte sdiag_family;
+ private final byte sdiag_protocol;
+ private final StructInetDiagSockId id;
+ private final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
+
+
+ public StructInetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote,
+ int family) {
+ sdiag_family = (byte) family;
+ sdiag_protocol = (byte) protocol;
+ id = new StructInetDiagSockId(local, remote);
+ }
+
+ public void pack(ByteBuffer byteBuffer) {
+ // The ByteOrder must have already been set by the caller.
+ byteBuffer.put((byte) sdiag_family);
+ byteBuffer.put((byte) sdiag_protocol);
+ byteBuffer.put((byte) 0);
+ byteBuffer.put((byte) 0);
+ byteBuffer.putInt(INET_DIAG_REQ_V2_ALL_STATES);
+ id.pack(byteBuffer);
+ }
+
+ @Override
+ public String toString() {
+ final String familyStr = NetlinkConstants.stringForAddressFamily(sdiag_family);
+ final String protocolStr = NetlinkConstants.stringForAddressFamily(sdiag_protocol);
+
+ return "StructInetDiagReqV2{ "
+ + "sdiag_family{" + familyStr + "}, "
+ + "sdiag_protocol{" + protocolStr + "}, "
+ + "idiag_ext{" + 0 + ")}, "
+ + "pad{" + 0 + "}, "
+ + "idiag_states{" + Integer.toHexString(INET_DIAG_REQ_V2_ALL_STATES) + "}, "
+ + id.toString()
+ + "}";
+ }
+}
diff --git a/services/net/java/android/net/netlink/StructInetDiagSockId.java b/services/net/java/android/net/netlink/StructInetDiagSockId.java
new file mode 100644
index 0000000..2e9fa25
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructInetDiagSockId.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 android.net.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * struct inet_diag_req_v2
+ *
+ * see <linux_src>/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_sockid {
+ * __be16 idiag_sport;
+ * __be16 idiag_dport;
+ * __be32 idiag_src[4];
+ * __be32 idiag_dst[4];
+ * __u32 idiag_if;
+ * __u32 idiag_cookie[2];
+ * #define INET_DIAG_NOCOOKIE (~0U)
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagSockId {
+ public static final int STRUCT_SIZE = 48;
+
+ private final InetSocketAddress mLocSocketAddress;
+ private final InetSocketAddress mRemSocketAddress;
+ private final byte[] INET_DIAG_NOCOOKIE = new byte[]{
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ private final byte[] IPV4_PADDING = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ public StructInetDiagSockId(InetSocketAddress loc, InetSocketAddress rem) {
+ mLocSocketAddress = loc;
+ mRemSocketAddress = rem;
+ }
+
+ public void pack(ByteBuffer byteBuffer) {
+ byteBuffer.order(BIG_ENDIAN);
+ byteBuffer.putShort((short) mLocSocketAddress.getPort());
+ byteBuffer.putShort((short) mRemSocketAddress.getPort());
+ byteBuffer.put(mLocSocketAddress.getAddress().getAddress());
+ if (mLocSocketAddress.getAddress() instanceof Inet4Address) {
+ byteBuffer.put(IPV4_PADDING);
+ }
+ byteBuffer.put(mRemSocketAddress.getAddress().getAddress());
+ if (mRemSocketAddress.getAddress() instanceof Inet4Address) {
+ byteBuffer.put(IPV4_PADDING);
+ }
+ byteBuffer.order(ByteOrder.nativeOrder());
+ byteBuffer.putInt(0);
+ byteBuffer.put(INET_DIAG_NOCOOKIE);
+ }
+
+ @Override
+ public String toString() {
+ return "StructInetDiagSockId{ "
+ + "idiag_sport{" + mLocSocketAddress.getPort() + "}, "
+ + "idiag_dport{" + mRemSocketAddress.getPort() + "}, "
+ + "idiag_src{" + mLocSocketAddress.getAddress().getHostAddress() + "}, "
+ + "idiag_dst{" + mRemSocketAddress.getAddress().getHostAddress() + "}, "
+ + "idiag_if{" + 0 + "} "
+ + "idiag_cookie{INET_DIAG_NOCOOKIE}"
+ + "}";
+ }
+}
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index f7bf393..5a73a4e 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -17,6 +17,7 @@
package android.net.util;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -92,10 +93,17 @@
}
/**
- * Log an error due to an exception, with the exception stacktrace.
+ * Log an error due to an exception, with the exception stacktrace if provided.
+ *
+ * <p>The error and exception message appear in the shared log, but the stacktrace is only
+ * logged in general log output (logcat).
*/
- public void e(@NonNull String msg, @NonNull Throwable e) {
- Log.e(mTag, record(Category.ERROR, msg + ": " + e.getMessage()), e);
+ public void e(@NonNull String msg, @Nullable Throwable exception) {
+ if (exception == null) {
+ e(msg);
+ return;
+ }
+ Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
}
public void i(String msg) {
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 8b59771..78c0be4 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -61,6 +61,7 @@
$(call all-Iaidl-files-under, $(INTERNAL_BACKUP)) \
$(call all-java-files-under, ../../core/java/android/app/backup) \
$(call all-Iaidl-files-under, ../../core/java/android/app/backup) \
+ $(call all-java-files-under, ../../core/java/android/util/proto) \
../../core/java/android/content/pm/PackageInfo.java \
../../core/java/android/app/IBackupAgent.aidl \
../../core/java/android/util/KeyValueSettingObserver.java \
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 57ebbfc..de915ab 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -122,6 +122,13 @@
ShadowAppBackupUtils.reset();
}
+ @Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
/* Tests for destination string */
@Test
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
new file mode 100644
index 0000000..383bf1d
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.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.server.backup.encryption.chunk;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.Preconditions;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.base.Charsets;
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+// Include android.util.proto in addition to classes under test because the latest versions of
+// android.util.proto.Proto{Input|Output}Stream are not part of Robolectric.
+@SystemLoaderPackages({"com.android.server.backup", "android.util.proto"})
+@Presubmit
+public class ChunkListingTest {
+ private static final String CHUNK_A = "CHUNK_A";
+ private static final String CHUNK_B = "CHUNK_B";
+ private static final String CHUNK_C = "CHUNK_C";
+
+ private static final int CHUNK_A_LENGTH = 256;
+ private static final int CHUNK_B_LENGTH = 1024;
+ private static final int CHUNK_C_LENGTH = 4055;
+
+ private ChunkHash mChunkHashA;
+ private ChunkHash mChunkHashB;
+ private ChunkHash mChunkHashC;
+
+ @Before
+ public void setUp() throws Exception {
+ mChunkHashA = getHash(CHUNK_A);
+ mChunkHashB = getHash(CHUNK_B);
+ mChunkHashC = getHash(CHUNK_C);
+ }
+
+ @Test
+ public void testHasChunk_whenChunkInListing_returnsTrue() throws Exception {
+ byte[] chunkListingProto =
+ createChunkListingProto(
+ new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
+ new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
+ ChunkListing chunkListing =
+ ChunkListing.readFromProto(
+ new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
+
+ boolean chunkAInList = chunkListing.hasChunk(mChunkHashA);
+ boolean chunkBInList = chunkListing.hasChunk(mChunkHashB);
+ boolean chunkCInList = chunkListing.hasChunk(mChunkHashC);
+
+ assertThat(chunkAInList).isTrue();
+ assertThat(chunkBInList).isTrue();
+ assertThat(chunkCInList).isTrue();
+ }
+
+ @Test
+ public void testHasChunk_whenChunkNotInListing_returnsFalse() throws Exception {
+ byte[] chunkListingProto =
+ createChunkListingProto(
+ new ChunkHash[] {mChunkHashA, mChunkHashB},
+ new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH});
+ ChunkListing chunkListing =
+ ChunkListing.readFromProto(
+ new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
+ ChunkHash chunkHashEmpty = getHash("");
+
+ boolean chunkCInList = chunkListing.hasChunk(mChunkHashC);
+ boolean emptyChunkInList = chunkListing.hasChunk(chunkHashEmpty);
+
+ assertThat(chunkCInList).isFalse();
+ assertThat(emptyChunkInList).isFalse();
+ }
+
+ @Test
+ public void testGetChunkEntry_returnsEntryWithCorrectLength() throws Exception {
+ byte[] chunkListingProto =
+ createChunkListingProto(
+ new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
+ new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
+ ChunkListing chunkListing =
+ ChunkListing.readFromProto(
+ new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
+
+ ChunkListing.Entry entryA = chunkListing.getChunkEntry(mChunkHashA);
+ ChunkListing.Entry entryB = chunkListing.getChunkEntry(mChunkHashB);
+ ChunkListing.Entry entryC = chunkListing.getChunkEntry(mChunkHashC);
+
+ assertThat(entryA.getLength()).isEqualTo(CHUNK_A_LENGTH);
+ assertThat(entryB.getLength()).isEqualTo(CHUNK_B_LENGTH);
+ assertThat(entryC.getLength()).isEqualTo(CHUNK_C_LENGTH);
+ }
+
+ @Test
+ public void testGetChunkEntry_returnsEntryWithCorrectStart() throws Exception {
+ byte[] chunkListingProto =
+ createChunkListingProto(
+ new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
+ new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
+ ChunkListing chunkListing =
+ ChunkListing.readFromProto(
+ new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
+
+ ChunkListing.Entry entryA = chunkListing.getChunkEntry(mChunkHashA);
+ ChunkListing.Entry entryB = chunkListing.getChunkEntry(mChunkHashB);
+ ChunkListing.Entry entryC = chunkListing.getChunkEntry(mChunkHashC);
+
+ assertThat(entryA.getStart()).isEqualTo(0);
+ assertThat(entryB.getStart()).isEqualTo(CHUNK_A_LENGTH);
+ assertThat(entryC.getStart()).isEqualTo(CHUNK_A_LENGTH + CHUNK_B_LENGTH);
+ }
+
+ @Test
+ public void testGetChunkEntry_returnsNullForNonExistentChunk() throws Exception {
+ byte[] chunkListingProto =
+ createChunkListingProto(
+ new ChunkHash[] {mChunkHashA, mChunkHashB},
+ new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH});
+ ChunkListing chunkListing =
+ ChunkListing.readFromProto(
+ new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
+
+ ChunkListing.Entry chunkEntryNonexistentChunk = chunkListing.getChunkEntry(mChunkHashC);
+
+ assertThat(chunkEntryNonexistentChunk).isNull();
+ }
+
+ @Test
+ public void testReadFromProto_whenEmptyProto_returnsChunkListingWith0Chunks() throws Exception {
+ ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {}));
+
+ ChunkListing chunkListing = ChunkListing.readFromProto(emptyProto);
+
+ assertThat(chunkListing.getChunkCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testReadFromProto_returnsChunkListingWithCorrectSize() throws Exception {
+ byte[] chunkListingProto =
+ createChunkListingProto(
+ new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
+ new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
+
+ ChunkListing chunkListing =
+ ChunkListing.readFromProto(
+ new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
+
+ assertThat(chunkListing.getChunkCount()).isEqualTo(3);
+ }
+
+ private byte[] createChunkListingProto(ChunkHash[] hashes, int[] lengths) {
+ Preconditions.checkArgument(hashes.length == lengths.length);
+ ProtoOutputStream outputStream = new ProtoOutputStream();
+
+ for (int i = 0; i < hashes.length; ++i) {
+ writeToProtoOutputStream(outputStream, hashes[i], lengths[i]);
+ }
+ outputStream.flush();
+
+ return outputStream.getBytes();
+ }
+
+ private void writeToProtoOutputStream(ProtoOutputStream out, ChunkHash chunkHash, int length) {
+ long token = out.start(ChunksMetadataProto.ChunkListing.CHUNKS);
+ out.write(ChunksMetadataProto.Chunk.HASH, chunkHash.getHash());
+ out.write(ChunksMetadataProto.Chunk.LENGTH, length);
+ out.end(token);
+ }
+
+ private ChunkHash getHash(String name) {
+ return new ChunkHash(
+ Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java
new file mode 100644
index 0000000..1dd7dc8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.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.backup.encryption.chunk;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.base.Charsets;
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+// Include android.util.proto in addition to classes under test because the latest versions of
+// android.util.proto.Proto{Input|Output}Stream are not part of Robolectric.
+@SystemLoaderPackages({"com.android.server.backup", "android.util.proto"})
+@Presubmit
+public class ChunkTest {
+ private static final String CHUNK_A = "CHUNK_A";
+ private static final int CHUNK_A_LENGTH = 256;
+
+ private ChunkHash mChunkHashA;
+
+ @Before
+ public void setUp() throws Exception {
+ mChunkHashA = getHash(CHUNK_A);
+ }
+
+ @Test
+ public void testReadFromProto_readsCorrectly() throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
+ out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
+ out.flush();
+ byte[] protoBytes = out.getBytes();
+
+ Chunk chunk =
+ Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
+
+ assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
+ assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
+ }
+
+ @Test
+ public void testReadFromProto_whenFieldsWrittenInReversedOrder_readsCorrectly()
+ throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ // Write fields of Chunk proto in reverse order.
+ out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
+ out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
+ out.flush();
+ byte[] protoBytes = out.getBytes();
+
+ Chunk chunk =
+ Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
+
+ assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
+ assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
+ }
+
+ @Test
+ public void testReadFromProto_whenEmptyProto_returnsEmptyHash() throws Exception {
+ ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {}));
+
+ Chunk chunk = Chunk.readFromProto(emptyProto);
+
+ assertThat(chunk.getHash()).asList().hasSize(0);
+ assertThat(chunk.getLength()).isEqualTo(0);
+ }
+
+ @Test
+ public void testReadFromProto_whenOnlyHashSet_returnsChunkWithOnlyHash() throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
+ out.flush();
+ byte[] protoBytes = out.getBytes();
+
+ Chunk chunk =
+ Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
+
+ assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
+ assertThat(chunk.getLength()).isEqualTo(0);
+ }
+
+ @Test
+ public void testReadFromProto_whenOnlyLengthSet_returnsChunkWithOnlyLength() throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
+ out.flush();
+ byte[] protoBytes = out.getBytes();
+
+ Chunk chunk =
+ Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
+
+ assertThat(chunk.getHash()).isEqualTo(new byte[] {});
+ assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
+ }
+
+ private ChunkHash getHash(String name) {
+ return new ChunkHash(
+ Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
new file mode 100644
index 0000000..1cd1528
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.encryption.chunk;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.primitives.Bytes;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class EncryptedChunkOrderingTest {
+ private static final byte[] TEST_BYTE_ARRAY_1 = new byte[] {1, 2, 3, 4, 5};
+ private static final byte[] TEST_BYTE_ARRAY_2 = new byte[] {5, 4, 3, 2, 1};
+
+ @Test
+ public void testEncryptedChunkOrdering_returnsValue() {
+ EncryptedChunkOrdering encryptedChunkOrdering =
+ EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
+
+ byte[] bytes = encryptedChunkOrdering.encryptedChunkOrdering();
+
+ assertThat(bytes)
+ .asList()
+ .containsExactlyElementsIn(Bytes.asList(TEST_BYTE_ARRAY_1))
+ .inOrder();
+ }
+
+ @Test
+ public void testEquals() {
+ EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
+ EncryptedChunkOrdering equalChunkOrdering1 =
+ EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
+ EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2);
+
+ assertThat(chunkOrdering1).isEqualTo(equalChunkOrdering1);
+ assertThat(chunkOrdering1).isNotEqualTo(chunkOrdering2);
+ }
+
+ @Test
+ public void testHashCode() {
+ EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
+ EncryptedChunkOrdering equalChunkOrdering1 =
+ EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1);
+ EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2);
+
+ int hash1 = chunkOrdering1.hashCode();
+ int equalHash1 = equalChunkOrdering1.hashCode();
+ int hash2 = chunkOrdering2.hashCode();
+
+ assertThat(hash1).isEqualTo(equalHash1);
+ assertThat(hash1).isNotEqualTo(hash2);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
new file mode 100644
index 0000000..3730335
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class AgentExceptionTest {
+ @Test
+ public void testTransitory_isTransitory() throws Exception {
+ AgentException exception = AgentException.transitory();
+
+ assertThat(exception.isTransitory()).isTrue();
+ }
+
+ @Test
+ public void testTransitory_withCause() throws Exception {
+ Exception cause = new IOException();
+
+ AgentException exception = AgentException.transitory(cause);
+
+ assertThat(exception.isTransitory()).isTrue();
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testPermanent_isNotTransitory() throws Exception {
+ AgentException exception = AgentException.permanent();
+
+ assertThat(exception.isTransitory()).isFalse();
+ }
+
+ @Test
+ public void testPermanent_withCause() throws Exception {
+ Exception cause = new IOException();
+
+ AgentException exception = AgentException.permanent(cause);
+
+ assertThat(exception.isTransitory()).isFalse();
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
new file mode 100644
index 0000000..5ea74f1
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
@@ -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
+ */
+
+package com.android.server.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class BackupExceptionTest {
+ @Test
+ public void testConstructor_passesCause() {
+ Exception cause = new IOException();
+
+ Exception exception = new BackupException(cause);
+
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+}
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 21b90f1..31e8333 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -62,6 +62,13 @@
}
@Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = KeyValueBackupReporter.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
+ @Test
public void testOnNewThread_logsCorrectly() throws Exception {
KeyValueBackupReporter.onNewThread("foo");
@@ -81,39 +88,4 @@
assertThat(observer).isEqualTo(mObserver);
}
-
- @Test
- public void testOnRevertTask_logsCorrectly() throws Exception {
- setMoreDebug(true);
-
- mReporter.onRevertTask();
-
- assertLogcat(TAG, Log.INFO);
- }
-
- @Test
- public void testOnRemoteCallReturned_logsCorrectly() throws Exception {
- setMoreDebug(true);
-
- mReporter.onRemoteCallReturned(RemoteResult.of(3), "onFoo()");
-
- assertLogcat(TAG, Log.VERBOSE);
- ShadowLog.LogItem log = ShadowLog.getLogsForTag(TAG).get(0);
- assertThat(log.msg).contains("onFoo()");
- assertThat(log.msg).contains("3");
- }
-
- /**
- * HACK: We actually want {@link KeyValueBackupReporter#MORE_DEBUG} to be a constant to be able
- * to strip those lines at build time. So, we have to do this to test :(
- */
- private static void setMoreDebug(boolean value)
- throws NoSuchFieldException, IllegalAccessException {
- if (KeyValueBackupReporter.MORE_DEBUG == value) {
- return;
- }
- Field moreDebugField = KeyValueBackupReporter.class.getDeclaredField("MORE_DEBUG");
- moreDebugField.setAccessible(true);
- moreDebugField.set(null, value);
- }
}
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 b4bc9d1..fb57d68 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -155,9 +155,7 @@
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
-// TODO: When returning to RUNNING_QUEUE vs FINAL, RUNNING_QUEUE sets status = OK. Why? Verify?
-// TODO: Check queue in general, behavior w/ multiple packages
-// TODO: Test PM invocation
+// TODO: Test agents timing out
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
@@ -370,6 +368,47 @@
}
@Test
+ public void testRunTask_whenOnePackage_cleansUpPmFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
+ public void testRunTask_whenTransportReturnsTransportErrorForPm_cleansUpPmFiles()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+ .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
+ public void testRunTask_whenTransportReturnsTransportErrorForPm_resetsBackupState()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+ .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+ }
+
+ @Test
public void testRunTask_whenOnePackage_updatesBookkeeping() throws Exception {
// Transport has to be initialized to not reset current token
TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -418,7 +457,7 @@
public void testRunTask_whenNonPmPackageAndNonIncremental_doesNotBackUpPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ BackupAgent pmAgent = spy(createPmAgent());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
@@ -431,7 +470,7 @@
public void testRunTask_whenNonPmPackageAndPmAndNonIncremental_backsUpPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ BackupAgent pmAgent = spy(createPmAgent());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
KeyValueBackupTask task =
createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE);
@@ -445,7 +484,7 @@
public void testRunTask_whenNonPmPackageAndIncremental_backsUpPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ BackupAgent pmAgent = spy(createPmAgent());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1);
@@ -529,6 +568,35 @@
}
@Test
+ public void testRunTask_whenPackageUnknown() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ // Not calling setUpAgent() for PACKAGE_1
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(transportMock.transport, never())
+ .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+ verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
+ verify(mObserver).backupFinished(SUCCESS);
+ assertBackupNotPendingFor(PACKAGE_1);
+ }
+
+ @Test
+ public void testRunTask_whenFirstPackageUnknown_callsTransportForSecondPackage()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ // Not calling setUpAgent() for PACKAGE_1
+ setUpAgentWithData(PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+ }
+
+ @Test
public void testRunTask_whenPackageNotEligibleForBackup() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgentWithData(PACKAGE_1.backupNotAllowed());
@@ -545,6 +613,19 @@
}
@Test
+ public void testRunTask_whenFirstPackageNotEligibleForBackup_callsTransportForSecondPackage()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentsWithData(PACKAGE_1.backupNotAllowed(), PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+ }
+
+ @Test
public void testRunTask_whenPackageDoesFullBackup() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
PackageData packageData = fullBackupPackage(1);
@@ -561,6 +642,20 @@
}
@Test
+ public void testRunTask_whenFirstPackageDoesFullBackup_callsTransportForSecondPackage()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ PackageData packageData = fullBackupPackage(1);
+ setUpAgentsWithData(packageData, PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, packageData, PACKAGE_2);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+ }
+
+ @Test
public void testRunTask_whenPackageIsStopped() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgentWithData(PACKAGE_1.stopped());
@@ -575,18 +670,16 @@
}
@Test
- public void testRunTask_whenPackageUnknown() throws Exception {
+ public void testRunTask_whenFirstPackageIsStopped_callsTransportForSecondPackage()
+ throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
- // Not calling setUpAgent()
- KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ setUpAgentsWithData(PACKAGE_1.stopped(), PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
runTask(task);
- verify(transportMock.transport, never())
- .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
- verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
- verify(mObserver).backupFinished(SUCCESS);
- assertBackupNotPendingFor(PACKAGE_1);
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
}
@Test
@@ -629,6 +722,7 @@
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -645,6 +739,7 @@
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -798,7 +893,7 @@
runTask(task);
- assertBackupNotPendingFor(PACKAGE_1);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -1140,6 +1235,38 @@
}
@Test
+ public void testRunTask_whenPmAgentWritesData_callsTransportPerformBackupWithAgentData()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ Path backupDataPath = createTemporaryFile();
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+ .then(copyBackupDataTo(backupDataPath));
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ agentOnBackupDo(
+ pmAgent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key1", "data1".getBytes());
+ writeData(dataOutput, "key2", "data2".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PM_PACKAGE)), any(), anyInt());
+ try (FileInputStream inputStream = new FileInputStream(backupDataPath.toFile())) {
+ BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
+ assertDataHasKeyValue(backupData, "key1", "data1".getBytes());
+ assertDataHasKeyValue(backupData, "key2", "data2".getBytes());
+ assertThat(backupData.readNextHeader()).isFalse();
+ }
+ }
+
+ @Test
public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -1176,6 +1303,50 @@
}
@Test
+ public void testRunTask_whenFinishBackupSucceedsForPm_cleansUp() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ agentOnBackupDo(
+ pmAgent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertThat(Files.readAllBytes(getStateFile(mTransport, PM_PACKAGE)))
+ .isEqualTo("newState".getBytes());
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ // We don't unbind PM
+ verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+ }
+
+ @Test
+ public void testRunTask_whenFinishBackupSucceedsForPm_doesNotUnbindPm() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ agentOnBackupDo(
+ pmAgent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+ }
+
+ @Test
public void testRunTask_whenFinishBackupSucceeds_logsBackupPackageEvent() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
@@ -1354,6 +1525,7 @@
public void testRunTask_whenTransportReturnsQuotaExceeded_updatesBookkeeping()
throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
when(transportMock.transport.performBackup(
argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
.thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
@@ -1701,9 +1873,9 @@
}
@Test
- public void testRunTask_whenPmAgentFails() throws Exception {
+ public void testRunTask_whenPmAgentFails_reportsCorrectly() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
- PackageManagerBackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
@@ -1718,6 +1890,75 @@
}
@Test
+ public void testRunTask_whenPmAgentFails_revertsTask() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertTaskReverted(transportMock, PACKAGE_1);
+ }
+
+ @Test
+ public void testRunTask_whenPmAgentFails_cleansUpFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
+ public void testRunTask_whenPmAgentFails_resetsBackupState() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+ }
+
+ @Test
+ public void testRunTask_whenMarkCancelDuringPmOnBackup_resetsBackupState() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ agentOnBackupDo(
+ pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
+
+ runTask(task);
+
+ verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+ }
+
+ @Test
+ public void testRunTask_whenMarkCancelDuringPmOnBackup_cleansUpFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ agentOnBackupDo(
+ pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
public void testRunTask_whenBackupRunning_doesNotThrow() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(mBackupManagerService.isBackupOperationInProgress()).thenReturn(true);
@@ -1736,7 +1977,7 @@
runTask(task);
- verify(mReporter).onReadAgentDataError(eq(PACKAGE_1.packageName), any());
+ verify(mReporter).onAgentDataError(eq(PACKAGE_1.packageName), any());
}
@Test
@@ -1779,6 +2020,24 @@
}
@Test
+ public void testRunTask_whenMarkCancelDuringAgentOnBackup_cleansUpFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ agentOnBackupDo(
+ agentMock,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ runInWorkerThread(task::markCancel);
+ });
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PACKAGE_1);
+ }
+
+ @Test
public void
testRunTask_whenMarkCancelDuringFirstAgentOnBackup_doesNotCallTransportAfterWaitCancel()
throws Exception {
@@ -2293,20 +2552,28 @@
*/
private static void agentOnBackupDo(AgentMock agentMock, BackupAgentOnBackup function)
throws Exception {
- doAnswer(
- (BackupAgentOnBackup)
- (oldState, dataOutput, newState) -> {
- ByteArrayOutputStream outputStream =
- new ByteArrayOutputStream();
- transferStreamedData(
- new FileInputStream(oldState.getFileDescriptor()),
- outputStream);
- agentMock.oldState = outputStream.toByteArray();
- agentMock.oldStateHistory.add(agentMock.oldState);
- function.onBackup(oldState, dataOutput, newState);
- })
- .when(agentMock.agent)
- .onBackup(any(), any(), any());
+ agentOnBackupDo(
+ agentMock.agent,
+ (oldState, dataOutput, newState) -> {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ transferStreamedData(
+ new FileInputStream(oldState.getFileDescriptor()), outputStream);
+ agentMock.oldState = outputStream.toByteArray();
+ agentMock.oldStateHistory.add(agentMock.oldState);
+ function.onBackup(oldState, dataOutput, newState);
+ });
+ }
+
+ /**
+ * Implements {@code function} for {@link BackupAgent#onBackup(ParcelFileDescriptor,
+ * BackupDataOutput, ParcelFileDescriptor)} of {@code agentMock}.
+ *
+ * @see #agentOnBackupDo(AgentMock, BackupAgentOnBackup)
+ * @see #remoteAgentOnBackupThrows(AgentMock, BackupAgentOnBackup)
+ */
+ private static void agentOnBackupDo(BackupAgent backupAgent, BackupAgentOnBackup function)
+ throws IOException {
+ doAnswer(function).when(backupAgent).onBackup(any(), any(), any());
}
/**
@@ -2400,6 +2667,10 @@
// constructor
assertJournalDoesNotContain(mBackupManagerService.getJournal(), packageName);
assertThat(mBackupManagerService.getPendingBackups()).doesNotContainKey(packageName);
+ // Also verifying BMS is never called since for some cases the package wouldn't be
+ // pending for other reasons (for example it's not eligible for backup). Regardless of
+ // these reasons, we shouldn't mark them as pending backup (call dataChangedImpl()).
+ verify(mBackupManagerService, never()).dataChangedImpl(packageName);
}
}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
new file mode 100644
index 0000000..4b79657
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.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.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class TaskExceptionTest {
+ @Test
+ public void testStateCompromised() {
+ TaskException exception = TaskException.stateCompromised();
+
+ assertThat(exception.isStateCompromised()).isTrue();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testStateCompromised_whenCauseInstanceOfTaskException() {
+ Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ TaskException exception = TaskException.stateCompromised(cause);
+
+ assertThat(exception.isStateCompromised()).isTrue();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testStateCompromised_whenCauseNotInstanceOfTaskException() {
+ Exception cause = new IOException();
+
+ TaskException exception = TaskException.stateCompromised(cause);
+
+ assertThat(exception.isStateCompromised()).isTrue();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testForStatus_whenTransportOk_throws() {
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> TaskException.forStatus(BackupTransport.TRANSPORT_OK));
+ }
+
+ @Test
+ public void testForStatus_whenTransportNotInitialized() {
+ TaskException exception =
+ TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ assertThat(exception.isStateCompromised()).isFalse();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+ }
+
+ @Test
+ public void testCausedBy_whenCauseInstanceOfTaskException_returnsCause() {
+ Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ TaskException exception = TaskException.causedBy(cause);
+
+ assertThat(exception).isEqualTo(cause);
+ }
+
+ @Test
+ public void testCausedBy_whenCauseNotInstanceOfTaskException() {
+ Exception cause = new IOException();
+
+ TaskException exception = TaskException.causedBy(cause);
+
+ assertThat(exception).isNotEqualTo(cause);
+ assertThat(exception.isStateCompromised()).isFalse();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testCreate() {
+ TaskException exception = TaskException.create();
+
+ assertThat(exception.isStateCompromised()).isFalse();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testIsStateCompromised_whenStateCompromised_returnsTrue() {
+ TaskException taskException = TaskException.stateCompromised();
+
+ boolean stateCompromised = taskException.isStateCompromised();
+
+ assertThat(stateCompromised).isTrue();
+ }
+
+ @Test
+ public void testIsStateCompromised_whenCreatedWithCreate_returnsFalse() {
+ TaskException taskException = TaskException.create();
+
+ boolean stateCompromised = taskException.isStateCompromised();
+
+ assertThat(stateCompromised).isFalse();
+ }
+
+ @Test
+ public void testGetStatus_whenStatusIsTransportPackageRejected() {
+ TaskException taskException =
+ TaskException.forStatus(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+
+ int status = taskException.getStatus();
+
+ assertThat(status).isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ }
+
+ @Test
+ public void testGetStatus_whenStatusIsTransportNotInitialized() {
+ TaskException taskException =
+ TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ int status = taskException.getStatus();
+
+ assertThat(status).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/src/com/android/server/backup/testing/TransportData.java
index 4c67180..77f5d9a4 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportData.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportData.java
@@ -48,9 +48,9 @@
public static TransportData localTransport() {
return new TransportData(
- "android/com.android.internal.backup.LocalTransport",
- "android/com.android.internal.backup.LocalTransportService",
- "com.android.internal.backup.LocalTransport",
+ "com.android.localtransport/.LocalTransport",
+ "com.android.localtransport/.LocalTransportService",
+ "com.android.localtransport.LocalTransport",
null,
"Backing up to debug-only private cache",
null,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 47ce879..c44e492 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -258,7 +258,7 @@
uidRec.hasInternetPermission = true;
mAms.mActiveUids.put(uid, uidRec);
- final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid);
+ final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid, null);
appRec.thread = Mockito.mock(IApplicationThread.class);
mAms.mLruProcesses.add(appRec);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index f6e5601..ffc7fa2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -188,17 +188,13 @@
@Test
public void testCanBeLaunchedOnDisplay() throws Exception {
- testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
- true /*activityResizeable*/, true /*expected*/);
+ mService.mSupportsMultiWindow = true;
+ final ActivityRecord activity = new ActivityBuilder(mService).build();
- testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
- false /*activityResizeable*/, false /*expected*/);
-
- testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/,
- true /*activityResizeable*/, false /*expected*/);
-
- testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/,
- false /*activityResizeable*/, true /*expected*/);
+ // An activity can be launched on default display.
+ assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
+ // An activity cannot be launched on a non-existent display.
+ assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
}
@Test
@@ -229,26 +225,4 @@
assertNull(mActivity.pendingOptions);
assertNotNull(activity2.pendingOptions);
}
-
- private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
- boolean activityResizeable, boolean expected) {
- mService.mSupportsMultiWindow = true;
-
- final TaskRecord task = taskPresent
- ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
-
- if (task != null) {
- task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
- }
-
- final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
- record.info.resizeMode = activityResizeable
- ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-
- record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
-
-
- verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
- anyInt(), anyInt(), eq(record.info));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index e1ebbcf..9192d6b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -83,7 +83,7 @@
final WindowProcessController wpc = new WindowProcessController(mService,
mService.mContext.getApplicationInfo(), "name", 12345,
UserHandle.getUserId(12345), mock(Object.class),
- mock(WindowProcessListener.class));
+ mock(WindowProcessListener.class), null);
wpc.setThread(mock(IApplicationThread.class));
mController.addPendingActivityLaunch(
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 749403e..bac4a52 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -208,7 +208,7 @@
// If no caller app, return {@code null} {@link ProcessRecord}.
final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new ProcessRecord(service.mAm, mock(ApplicationInfo.class), null, 0);
+ ? null : new ProcessRecord(service.mAm, mock(ApplicationInfo.class), null, 0, null);
doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 9d09f5c..22add01 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -281,7 +281,7 @@
final WindowProcessController wpc = new WindowProcessController(mService,
mService.mContext.getApplicationInfo(), "name", 12345,
UserHandle.getUserId(12345), mock(Object.class),
- mock(WindowProcessListener.class));
+ mock(WindowProcessListener.class), null);
wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
return activity;
@@ -439,6 +439,10 @@
}
@Override
+ void updateUsageStats(ActivityRecord component, boolean resumed) {
+ }
+
+ @Override
final protected ActivityStackSupervisor createStackSupervisor() {
final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
final KeyguardController keyguardController = mock(KeyguardController.class);
@@ -497,10 +501,6 @@
}
@Override
- void updateUsageStats(ActivityRecord component, boolean resumed) {
- }
-
- @Override
Configuration getGlobalConfiguration() {
return mContext.getResources().getConfiguration();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 87d367f..3819e21 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -67,7 +67,7 @@
@UiThreadTest
public void testCreateWorks() throws Exception {
AppErrorDialog.Data data = new AppErrorDialog.Data();
- data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
+ data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345, null);
data.result = new AppErrorResult();
AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
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 06c7437..9a283fe 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -16,9 +16,13 @@
package com.android.server.am;
+import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -29,10 +33,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MemoryStatUtilTest {
- private String MEMORY_STAT_CONTENTS = String.join(
+ private static final String MEMORY_STAT_CONTENTS = String.join(
"\n",
"cache 96", // keep different from total_cache to catch reading wrong value
"rss 97", // keep different from total_rss to catch reading wrong value
@@ -67,7 +73,7 @@
"total_active_file 81920",
"total_unevictable 0");
- private String PROC_STAT_CONTENTS = String.join(
+ private static final String PROC_STAT_CONTENTS = String.join(
" ",
"1040",
"(system_server)",
@@ -92,7 +98,7 @@
"0",
"2206",
"1257177088",
- "3", // this is rss in bytes
+ "3", // this is RSS (number of pages)
"4294967295",
"2936971264",
"2936991289",
@@ -122,18 +128,65 @@
"3198889956",
"0");
+ private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n"
+ + "State:\tS (sleeping)\n"
+ + "Tgid:\t12088\n"
+ + "Pid:\t12088\n"
+ + "PPid:\t723\n"
+ + "TracerPid:\t0\n"
+ + "Uid:\t10083\t10083\t10083\t10083\n"
+ + "Gid:\t10083\t10083\t10083\t10083\n"
+ + "Ngid:\t0\n"
+ + "FDSize:\t128\n"
+ + "Groups:\t3003 9997 20083 50083 \n"
+ + "VmPeak:\t 4546844 kB\n"
+ + "VmSize:\t 4542636 kB\n"
+ + "VmLck:\t 0 kB\n"
+ + "VmPin:\t 0 kB\n"
+ + "VmHWM:\t 137668 kB\n" // RSS high watermark
+ + "VmRSS:\t 126776 kB\n"
+ + "RssAnon:\t 37860 kB\n"
+ + "RssFile:\t 88764 kB\n"
+ + "RssShmem:\t 152 kB\n"
+ + "VmData:\t 4125112 kB\n"
+ + "VmStk:\t 8192 kB\n"
+ + "VmExe:\t 24 kB\n"
+ + "VmLib:\t 102432 kB\n"
+ + "VmPTE:\t 1300 kB\n"
+ + "VmPMD:\t 36 kB\n"
+ + "VmSwap:\t 0 kB\n"
+ + "Threads:\t95\n"
+ + "SigQ:\t0/13641\n"
+ + "SigPnd:\t0000000000000000\n"
+ + "ShdPnd:\t0000000000000000\n"
+ + "SigBlk:\t0000000000001204\n"
+ + "SigIgn:\t0000000000000001\n"
+ + "SigCgt:\t00000006400084f8\n"
+ + "CapInh:\t0000000000000000\n"
+ + "CapPrm:\t0000000000000000\n"
+ + "CapEff:\t0000000000000000\n"
+ + "CapBnd:\t0000000000000000\n"
+ + "CapAmb:\t0000000000000000\n"
+ + "Seccomp:\t2\n"
+ + "Cpus_allowed:\tff\n"
+ + "Cpus_allowed_list:\t0-7\n"
+ + "Mems_allowed:\t1\n"
+ + "Mems_allowed_list:\t0\n"
+ + "voluntary_ctxt_switches:\t903\n"
+ + "nonvoluntary_ctxt_switches:\t104\n";
+
@Test
- public void testParseMemoryStatFromMemcg_parsesCorrectValues() throws Exception {
+ public void testParseMemoryStatFromMemcg_parsesCorrectValues() {
MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
- assertEquals(stat.pgfault, 1);
- assertEquals(stat.pgmajfault, 2);
- assertEquals(stat.rssInBytes, 3);
- assertEquals(stat.cacheInBytes, 4);
- assertEquals(stat.swapInBytes, 5);
+ assertEquals(1, stat.pgfault);
+ assertEquals(2, stat.pgmajfault);
+ assertEquals(3, stat.rssInBytes);
+ assertEquals(4, stat.cacheInBytes);
+ assertEquals(5, stat.swapInBytes);
}
@Test
- public void testParseMemoryStatFromMemcg_emptyMemoryStatContents() throws Exception {
+ public void testParseMemoryStatFromMemcg_emptyMemoryStatContents() {
MemoryStat stat = parseMemoryStatFromMemcg("");
assertNull(stat);
@@ -142,21 +195,56 @@
}
@Test
- public void testParseMemoryStatFromProcfs_parsesCorrectValues() throws Exception {
+ 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);
assertEquals(2, stat.pgmajfault);
- assertEquals(3, stat.rssInBytes);
+ assertEquals(3 * PAGE_SIZE, stat.rssInBytes);
assertEquals(0, stat.cacheInBytes);
assertEquals(0, stat.swapInBytes);
}
@Test
- public void testParseMemoryStatFromProcfs_emptyContents() throws Exception {
+ public void testParseMemoryStatFromProcfs_emptyContents() {
MemoryStat stat = parseMemoryStatFromProcfs("");
assertNull(stat);
stat = parseMemoryStatFromProcfs(null);
assertNull(stat);
}
+
+ @Test
+ public void testParseMemoryStatFromProcfs_invalidValue() {
+ String contents = String.join(" ", Collections.nCopies(24, "memory"));
+ assertNull(parseMemoryStatFromProcfs(contents));
+ }
+
+ @Test
+ public void testParseVmHWMFromProcfs_parsesCorrectValue() {
+ assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS) / BYTES_IN_KILOBYTE);
+ }
+
+ @Test
+ public void testParseVmHWMFromProcfs_emptyContents() {
+ assertEquals(0, parseVmHWMFromProcfs(""));
+
+ assertEquals(0, parseVmHWMFromProcfs(null));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
index 54f93a8..39cab8d 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -56,6 +56,11 @@
}
@Override
+ protected int getBindFlags() {
+ return Context.BIND_FOREGROUND_SERVICE;
+ }
+
+ @Override
protected IDeviceAdminService asInterface(IBinder binder) {
return (IDeviceAdminService) binder;
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index b238e43..d34f951 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -30,7 +30,7 @@
public class FakeWindowState implements WindowManagerPolicy.WindowState {
- private WindowFrames windowFrames;
+ private WindowFrames mWindowFrames = new WindowFrames();
public WindowManager.LayoutParams attrs;
public int displayId;
@@ -53,41 +53,40 @@
}
@Override
- public void computeFrameLw(WindowFrames windowFrames) {
- this.windowFrames = windowFrames;
+ public void computeFrameLw() {
}
@Override
public Rect getFrameLw() {
- return windowFrames.mParentFrame;
+ return mWindowFrames.mParentFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return windowFrames.mDisplayFrame;
+ return mWindowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return windowFrames.mOverscanFrame;
+ return mWindowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return windowFrames.mContentFrame;
+ return mWindowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return windowFrames.mVisibleFrame;
+ return mWindowFrames.mVisibleFrame;
}
public Rect getStableFrame() {
- return windowFrames.mStableFrame;
+ return mWindowFrames.mStableFrame;
}
public Rect getDecorFrame() {
- return windowFrames.mDecorFrame;
+ return mWindowFrames.mDecorFrame;
}
@Override
@@ -252,6 +251,11 @@
}
@Override
+ public WindowFrames getWindowFrames() {
+ return mWindowFrames;
+ }
+
+ @Override
public boolean isInputMethodTarget() {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5039e42..8ac2930 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,18 +21,32 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerSaveState;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.PowerManagerService.Injector;
+import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.batterysaver.BatterySavingStats;
import org.junit.Rule;
import org.mockito.Mock;
@@ -47,11 +61,19 @@
private static final boolean BATTERY_SAVER_ENABLED = true;
private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
- private @Mock BatterySaverPolicy mBatterySaverPolicy;
+ private @Mock BatterySaverPolicy mBatterySaverPolicyMock;
+ private @Mock LightsManager mLightsManagerMock;
+ private @Mock DisplayManagerInternal mDisplayManagerInternalMock;
+ private @Mock BatteryManagerInternal mBatteryManagerInternalMock;
+ private @Mock ActivityManagerInternal mActivityManagerInternalMock;
+ private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock;
+ private @Mock Notifier mNotifierMock;
private PowerManagerService mService;
private PowerSaveState mPowerSaveState;
private DisplayPowerRequest mDisplayPowerRequest;
+
+
@Rule
public void setUp() throws Exception {
super.setUp();
@@ -61,11 +83,51 @@
.setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
.setBrightnessFactor(BRIGHTNESS_FACTOR)
.build();
- when(mBatterySaverPolicy.getBatterySaverPolicy(
+ when(mBatterySaverPolicyMock.getBatterySaverPolicy(
eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS), anyBoolean()))
.thenReturn(mPowerSaveState);
+
mDisplayPowerRequest = new DisplayPowerRequest();
- mService = new PowerManagerService(getContext(), mBatterySaverPolicy);
+ addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+ addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+ addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+ addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+
+ mService = new PowerManagerService(getContext(), new Injector() {
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return mNotifierMock;
+ }
+
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ return mock(SuspendBlocker.class);
+ }
+
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return mBatterySaverPolicyMock;
+ }
+
+ NativeWrapper createNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+ });
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(LightsManager.class);
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
}
@SmallTest
@@ -110,6 +172,25 @@
mService.setVrModeEnabled(false);
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
+ }
+ @SmallTest
+ public void testWakefulnessAwake_InitialValue() throws Exception {
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @SmallTest
+ public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+ // Start with AWAKE state
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.systemReady(null);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Take a nap with a flag.
+ mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
new file mode 100644
index 0000000..5a787ec
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS 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.usage;
+
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.fail;
+
+import static org.testng.Assert.assertEquals;
+
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+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.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UsageStatsDatabaseTest {
+ protected Context mContext;
+ private UsageStatsDatabase mUsageStatsDatabase;
+ private File mTestDir;
+
+ private IntervalStats mIntervalStats = new IntervalStats();
+ private long mEndTime = 0;
+
+ private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier =
+ new UsageStatsDatabase.StatCombiner<IntervalStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<IntervalStats> accResult) {
+ accResult.add(stats);
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
+ mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ mUsageStatsDatabase.init(1);
+ populateIntervalStats();
+ clearUsageStatsFiles();
+ }
+
+ /**
+ * A debugging utility for viewing the files currently in the test directory
+ */
+ private void clearUsageStatsFiles() {
+ File[] intervalDirs = mTestDir.listFiles();
+ for (File intervalDir : intervalDirs) {
+ if (intervalDir.isDirectory()) {
+ File[] usageFiles = intervalDir.listFiles();
+ for (File f : usageFiles) {
+ f.delete();
+ }
+ }
+ }
+ }
+
+ /**
+ * A debugging utility for viewing the files currently in the test directory
+ */
+ private String dumpUsageStatsFiles() {
+ StringBuilder sb = new StringBuilder();
+ File[] intervalDirs = mTestDir.listFiles();
+ for (File intervalDir : intervalDirs) {
+ if (intervalDir.isDirectory()) {
+ File[] usageFiles = intervalDir.listFiles();
+ for (File f : usageFiles) {
+ sb.append(f.toString());
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private void populateIntervalStats() {
+ final int numberOfEvents = 3000;
+ long time = 1;
+ mIntervalStats = new IntervalStats();
+
+ mIntervalStats.beginTime = 1;
+ mIntervalStats.interactiveTracker.count = 2;
+ mIntervalStats.interactiveTracker.duration = 111111;
+ mIntervalStats.nonInteractiveTracker.count = 3;
+ mIntervalStats.nonInteractiveTracker.duration = 222222;
+ mIntervalStats.keyguardShownTracker.count = 4;
+ mIntervalStats.keyguardShownTracker.duration = 333333;
+ mIntervalStats.keyguardHiddenTracker.count = 5;
+ mIntervalStats.keyguardHiddenTracker.duration = 4444444;
+
+ if (mIntervalStats.events == null) {
+ mIntervalStats.events = new EventList();
+ }
+
+ for (int i = 0; i < numberOfEvents; i++) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ final int packageInt = ((i / 3) % 7);
+ event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps"
+ if (packageInt == 3) {
+ // Third app is an instant app
+ event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
+ } else if (packageInt == 2 || packageInt == 4) {
+ event.mClass = ".fake.class.name" + i % 11;
+ }
+
+
+ event.mTimeStamp = time;
+ event.mEventType = i % 19; //"random" event type
+
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ //empty config,
+ event.mConfiguration = new Configuration();
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ //"random" shortcut
+ event.mShortcutId = "shortcut" + (i % 8);
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ //"random" bucket and reason
+ event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ //"random" channel
+ event.mNotificationChannelId = "channel" + (i % 5);
+ break;
+ }
+
+ mIntervalStats.events.insert(event);
+ mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+
+ time += 23; // Arbitrary progression of time
+ }
+ mEndTime = time;
+
+ Configuration config1 = new Configuration();
+ config1.fontScale = 3.3f;
+ config1.mcc = 4;
+ mIntervalStats.getOrCreateConfigurationStats(config1);
+
+ Configuration config2 = new Configuration();
+ config2.mnc = 5;
+ config2.setLocale(new Locale("en", "US"));
+ mIntervalStats.getOrCreateConfigurationStats(config2);
+
+ Configuration config3 = new Configuration();
+ config3.touchscreen = 6;
+ config3.keyboard = 7;
+ mIntervalStats.getOrCreateConfigurationStats(config3);
+
+ Configuration config4 = new Configuration();
+ config4.keyboardHidden = 8;
+ config4.hardKeyboardHidden = 9;
+ mIntervalStats.getOrCreateConfigurationStats(config4);
+
+ Configuration config5 = new Configuration();
+ config5.navigation = 10;
+ config5.navigationHidden = 11;
+ mIntervalStats.getOrCreateConfigurationStats(config5);
+
+ Configuration config6 = new Configuration();
+ config6.orientation = 12;
+ //Ignore screen layout, it's determined by locale
+ mIntervalStats.getOrCreateConfigurationStats(config6);
+
+ Configuration config7 = new Configuration();
+ config7.colorMode = 14;
+ config7.uiMode = 15;
+ mIntervalStats.getOrCreateConfigurationStats(config7);
+
+ Configuration config8 = new Configuration();
+ config8.screenWidthDp = 16;
+ config8.screenHeightDp = 17;
+ mIntervalStats.getOrCreateConfigurationStats(config8);
+
+ Configuration config9 = new Configuration();
+ config9.smallestScreenWidthDp = 18;
+ config9.densityDpi = 19;
+ mIntervalStats.getOrCreateConfigurationStats(config9);
+
+ mIntervalStats.activeConfiguration = config9;
+ }
+
+ void compareUsageStats(UsageStats us1, UsageStats us2) {
+ assertEquals(us1.mPackageName, us2.mPackageName);
+ // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
+ // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
+ assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+ // mLaunchCount not persisted, so skipped
+ assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
+ assertEquals(us1.mLastEvent, us2.mLastEvent);
+ assertEquals(us1.mChooserCounts, us2.mChooserCounts);
+ }
+
+ void compareUsageEvent(UsageEvents.Event e1, UsageEvents.Event e2, int debugId) {
+ assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
+ assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
+ assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
+ assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
+ switch (e1.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ assertEquals(e1.mConfiguration, e2.mConfiguration,
+ "Usage event " + debugId + e2.mConfiguration.toString());
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId);
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
+ "Usage event " + debugId);
+ break;
+ }
+ assertEquals(e1.mFlags, e2.mFlags);
+ }
+
+ void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) {
+ assertEquals(stats1.beginTime, stats2.beginTime);
+ assertEquals(stats1.endTime, stats2.endTime);
+ assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count);
+ assertEquals(stats1.interactiveTracker.duration, stats2.interactiveTracker.duration);
+ assertEquals(stats1.nonInteractiveTracker.count, stats2.nonInteractiveTracker.count);
+ assertEquals(stats1.nonInteractiveTracker.duration, stats2.nonInteractiveTracker.duration);
+ assertEquals(stats1.keyguardShownTracker.count, stats2.keyguardShownTracker.count);
+ assertEquals(stats1.keyguardShownTracker.duration, stats2.keyguardShownTracker.duration);
+ assertEquals(stats1.keyguardHiddenTracker.count, stats2.keyguardHiddenTracker.count);
+ assertEquals(stats1.keyguardHiddenTracker.duration, stats2.keyguardHiddenTracker.duration);
+
+ String[] usageKey1 = stats1.packageStats.keySet().toArray(new String[0]);
+ String[] usageKey2 = stats2.packageStats.keySet().toArray(new String[0]);
+ for (int i = 0; i < usageKey1.length; i++) {
+ UsageStats usageStats1 = stats1.packageStats.get(usageKey1[i]);
+ UsageStats usageStats2 = stats2.packageStats.get(usageKey2[i]);
+ compareUsageStats(usageStats1, usageStats2);
+ }
+
+ assertEquals(stats1.configurations.size(), stats2.configurations.size());
+ Configuration[] configSet1 = stats1.configurations.keySet().toArray(new Configuration[0]);
+ for (int i = 0; i < configSet1.length; i++) {
+ if (!stats2.configurations.containsKey(configSet1[i])) {
+ Configuration[] configSet2 = stats2.configurations.keySet().toArray(
+ new Configuration[0]);
+ String debugInfo = "";
+ for (Configuration c : configSet1) {
+ debugInfo += c.toString() + "\n";
+ }
+ debugInfo += "\n";
+ for (Configuration c : configSet2) {
+ debugInfo += c.toString() + "\n";
+ }
+ fail("Config " + configSet1[i].toString()
+ + " not found in deserialized IntervalStat\n" + debugInfo);
+ }
+ }
+ assertEquals(stats1.activeConfiguration, stats2.activeConfiguration);
+
+ assertEquals(stats1.events.size(), stats2.events.size());
+ for (int i = 0; i < stats1.events.size(); i++) {
+ compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i);
+ }
+ }
+
+ /**
+ * Runs the Write Read test.
+ * Will write the generated IntervalStat to disk, read it from disk and compare the two
+ */
+ void runWriteReadTest(int interval) throws IOException {
+ mUsageStatsDatabase.putUsageStats(interval, mIntervalStats);
+ List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+
+ assertEquals(1, stats.size());
+ compareIntervalStats(mIntervalStats, stats.get(0));
+ }
+
+ /**
+ * Demonstrate that IntervalStats can be serialized and deserialized from disk without loss of
+ * relevant data.
+ */
+ @Test
+ public void testWriteRead() throws IOException {
+ runWriteReadTest(UsageStatsManager.INTERVAL_DAILY);
+ runWriteReadTest(UsageStatsManager.INTERVAL_WEEKLY);
+ runWriteReadTest(UsageStatsManager.INTERVAL_MONTHLY);
+ runWriteReadTest(UsageStatsManager.INTERVAL_YEARLY);
+ }
+
+ /**
+ * Runs the Version Change tests.
+ * Will write the generated IntervalStat to disk in one version format, "upgrade" to another
+ * version and read the automatically upgraded files on disk in the new file format.
+ */
+ void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
+ // Write IntervalStats to disk in old version format
+ UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
+ prevDB.init(1);
+ prevDB.putUsageStats(interval, mIntervalStats);
+
+ // Simulate an upgrade to a new version and read from the disk
+ UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
+ newDB.init(mEndTime);
+ List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+
+ assertEquals(1, stats.size());
+ // The written and read IntervalStats should match
+ compareIntervalStats(mIntervalStats, stats.get(0));
+ }
+
+ /**
+ * Test the version upgrade from 3 to 4
+ */
+ @Test
+ public void testVersionUpgradeFrom3to4() throws IOException {
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY);
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY);
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY);
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index e7c45d5..088e229 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -24,6 +25,8 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static org.junit.Assert.fail;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
@@ -33,9 +36,11 @@
import android.os.Binder;
import android.os.IInterface;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseBooleanArray;
import android.view.IRecentsAnimationRunner;
import android.view.SurfaceControl;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -109,6 +114,25 @@
}
}
+ @Test
+ @FlakyTest(bugId = 117117823)
+ public void testIncludedApps_expectTargetAndVisible() throws Exception {
+ sWm.setRecentsAnimationController(mController);
+ final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ hiddenAppWindow.setHidden(true);
+ mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+
+ // Ensure that we are animating the target activity as well
+ assertTrue(mController.isAnimatingTask(homeAppWindow.getTask()));
+ assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ assertFalse(mController.isAnimatingTask(hiddenAppWindow.getTask()));
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 5fb8997..474e5b7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -458,12 +458,6 @@
}
@Override
- public void setLastInputMethodWindowLw(WindowState ime,
- WindowState target) {
-
- }
-
- @Override
public void showRecentApps() {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index e648230..0886729 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -202,8 +202,8 @@
// When mFrame extends past cf, the content insets are
// the difference between mFrame and ContentFrame. Visible
// and stable frames work the same way.
- final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -217,7 +217,7 @@
w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
w.mRequestedWidth = 100;
w.mRequestedHeight = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 100, 100, 200, 200);
assertContentInset(w, 0, 0, 0, 0);
// In this case the frames are shrunk to the window frame.
@@ -238,8 +238,8 @@
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
- w.computeFrameLw(windowFrames);
+ w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// It can select various widths and heights within the bounds.
@@ -247,14 +247,14 @@
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// Explicit width and height without requested width/height
// gets us nothing.
assertFrame(w, 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
assertFrame(w, 0, 0, 300, 300);
@@ -267,14 +267,14 @@
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
@@ -282,7 +282,7 @@
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
@@ -292,16 +292,16 @@
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 600, 600, 900, 900);
}
@@ -322,8 +322,9 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -336,7 +337,7 @@
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
int contentInsetRight = taskRight - cfRight;
int contentInsetBottom = taskBottom - cfBottom;
@@ -354,7 +355,7 @@
final int insetBottom = insetTop + (taskBottom - taskTop);
task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
@@ -384,13 +385,14 @@
// We use a decor content frame with insets to produce cropping.
Rect dcf = new Rect(cf);
- final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw();
assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
// Now we set up a window which doesn't fill the entire decor frame.
@@ -404,7 +406,7 @@
w.mAttrs.height = logicalHeight / 2;
w.mRequestedWidth = logicalWidth / 2;
w.mRequestedHeight = logicalHeight / 2;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// Normally the crop is shrunk from the decor frame
// to the computed window frame.
@@ -437,8 +439,9 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -455,7 +458,7 @@
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
assertContentFrame(w, cf);
assertContentInset(w, 0, 0, 0, 0);
@@ -473,9 +476,10 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
@@ -497,9 +501,10 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index b43d9a6..b7cc9ce 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -76,7 +76,8 @@
*/
@SmallTest
@FlakyTest(bugId = 74078662)
-@Presubmit
+// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
+// @Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowStateTests extends WindowTestsBase {
@@ -369,36 +370,45 @@
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
app.mHasSurface = true;
- app.mToken.mSurfaceControl = mock(SurfaceControl.class);
+ app.mSurfaceControl = mock(SurfaceControl.class);
try {
app.getFrameLw().set(10, 20, 60, 80);
+ app.updateSurfacePosition(t);
app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true);
assertTrue(app.mSeamlesslyRotated);
+
+ // Verify we un-rotate the window state surface.
Matrix matrix = new Matrix();
// Un-rotate 90 deg
matrix.setRotate(270);
// Translate it back to origin
matrix.postTranslate(0, mDisplayInfo.logicalWidth);
- verify(t).setMatrix(eq(app.mToken.mSurfaceControl), eq(matrix), any(float[].class));
+ verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
+
+ // Verify we update the position as well.
+ float[] currentSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
+ matrix.mapPoints(currentSurfacePos);
+ verify(t).setPosition(eq(app.mSurfaceControl), eq(currentSurfacePos[0]),
+ eq(currentSurfacePos[1]));
} finally {
+ app.mSurfaceControl = null;
app.mHasSurface = false;
- app.mToken.mSurfaceControl = null;
}
}
@Test
public void testDisplayCutoutIsCalculatedRelativeToFrame() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- WindowFrames wf = new WindowFrames();
+ WindowFrames wf = app.getWindowFrames();
wf.mParentFrame.set(7, 10, 185, 380);
wf.mDisplayFrame.set(wf.mParentFrame);
final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
- app.computeFrameLw(wf);
+ app.computeFrameLw();
assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
index bbc6550..01b7c4f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
@@ -59,7 +59,8 @@
*/
@SmallTest
@FlakyTest(bugId = 74078662)
-@Presubmit
+// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
+// @Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowTracingTest extends WindowTestsBase {
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 4344285..58aae2b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -97,6 +97,7 @@
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;
@@ -2191,6 +2192,26 @@
}
@Test
+ public void testDontAutogroupIfCritical() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
+ r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+
+ r = generateNotificationRecord(mTestNotificationChannel, 1, null, false);
+ r.setCriticality(CriticalNotificationExtractor.CRITICAL);
+ runnable = mService.new PostNotificationRunnable(r.getKey());
+ mService.addEnqueuedNotification(r);
+
+ runnable.run();
+ waitForIdle();
+
+ verify(mGroupHelper, never()).onNotificationPosted(any(), anyBoolean());
+ }
+
+ @Test
public void testNoFakeColorizedPermission() throws Exception {
when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED);
Notification.Builder nb = new Notification.Builder(mContext,
@@ -3382,10 +3403,44 @@
}
@Test
+ public void testIsCallerInstantApp_primaryUser() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+
+ assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+
+ info.privateFlags = 0;
+ assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+ }
+
+ @Test
+ public void testIsCallerInstantApp_secondaryUser() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+
+ assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+ }
+
+ @Test
+ public void testResolveNotificationUid_sameApp_nonSystemUser() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.uid = Binder.getCallingUid();
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+
+ int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 10);
+
+ assertEquals(info.uid, actualUid);
+ }
+
+ @Test
public void testResolveNotificationUid_sameApp() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = Binder.getCallingUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 0);
@@ -3393,17 +3448,14 @@
}
@Test
- public void testResolveNotificationUid_sameAppWrongPkg() throws Exception {
+ public void testResolveNotificationUid_sameAppDiffPackage() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = Binder.getCallingUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
- try {
- mService.resolveNotificationUid("caller", "other", info.uid, 0);
- fail("Incorrect pkg didn't throw security exception");
- } catch (SecurityException e) {
- // yay
- }
+ int actualUid = mService.resolveNotificationUid("caller", "callerAlso", info.uid, 0);
+
+ assertEquals(info.uid, actualUid);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 750345b..79998a5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -284,8 +284,8 @@
compareChannels(channel2,
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
- List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
+ List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -354,8 +354,8 @@
compareChannels(channel3,
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
- List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
+ List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -1350,8 +1350,8 @@
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
UID_N_MR1});
- assertEquals(0,
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, true, true, false).getList().size());
}
@Test
@@ -1440,8 +1440,8 @@
new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
+ List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, true, true, false).getList();
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
@@ -1473,13 +1473,13 @@
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false).getList();
channel1.setImportance(IMPORTANCE_LOW);
mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true);
- List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
+ List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, true, true, false).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -1490,6 +1490,32 @@
}
@Test
+ public void testGetChannelGroups_includeEmptyGroups() {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ NotificationChannelGroup ncgEmpty = new NotificationChannelGroup("group2", "name2");
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncgEmpty, true);
+
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+
+ List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, false, true).getList();
+
+ assertEquals(2, actual.size());
+ for (NotificationChannelGroup group : actual) {
+ if (Objects.equals(group.getId(), ncg.getId())) {
+ assertEquals(1, group.getChannels().size());
+ }
+ if (Objects.equals(group.getId(), ncgEmpty.getId())) {
+ assertEquals(0, group.getChannels().size());
+ }
+ }
+ }
+
+ @Test
public void testCreateChannel_updateName() throws Exception {
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
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 91f4bc8..13f3e5e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -45,6 +45,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -60,6 +61,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Xml;
import com.android.internal.R;
@@ -74,12 +76,16 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -88,7 +94,7 @@
ConditionProviders mConditionProviders;
@Mock NotificationManager mNotificationManager;
- @Mock private Resources mResources;
+ private Resources mResources;
private TestableLooper mTestableLooper;
private ZenModeHelper mZenModeHelperSpy;
private Context mContext;
@@ -101,11 +107,17 @@
mTestableLooper = TestableLooper.get(this);
mContext = spy(getContext());
mContentResolver = mContext.getContentResolver();
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getString(R.string.zen_mode_default_every_night_name)).thenReturn("night");
- when(mResources.getString(R.string.zen_mode_default_events_name)).thenReturn("events");
- when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
+ mResources = spy(mContext.getResources());
+ try {
+ when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
+ getDefaultConfigParser());
+ } catch (Exception e) {
+ Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" +
+ e.toString());
+ }
+
+ when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
mConditionProviders = new ConditionProviders(mContext, new UserProfiles(),
AppGlobals.getPackageManager());
mConditionProviders.addSystemProvider(new CountdownConditionProvider());
@@ -113,6 +125,30 @@
mConditionProviders));
}
+ private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException {
+ String xml = "<zen version=\"8\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"true\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<automatic ruleId=\"EVENTS_DEFAULT_RULE\" enabled=\"false\" snoozing=\"false\""
+ + " name=\"Event\" zen=\"1\""
+ + " component=\"android/com.android.server.notification.EventConditionProvider\""
+ + " conditionId=\"condition://android/event?userId=-10000&calendar=&"
+ + "reply=1\"/>\n"
+ + "<automatic ruleId=\"EVERY_NIGHT_DEFAULT_RULE\" enabled=\"false\""
+ + " snoozing=\"false\" name=\"Sleeping\" zen=\"1\""
+ + " component=\"android/com.android.server.notification.ScheduleConditionProvider\""
+ + " conditionId=\"condition://android/schedule?days=1.2.3.4.5.6.7 &start=22.0"
+ + "&end=7.0&exitAtAlarm=true\"/>"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ return new XmlResourceParserImpl(parser);
+ }
+
private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup, Integer version)
throws Exception {
XmlSerializer serializer = new FastXmlSerializer();
@@ -649,8 +685,8 @@
customRule.id = "customRule";
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- customRule.component = new ComponentName("test", "test");
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
automaticRules.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
@@ -662,8 +698,8 @@
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
mZenModeHelperSpy.readXml(parser, true);
- assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode());
assertEquals(original, mZenModeHelperSpy.mConfig);
+ assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode());
}
@Test
@@ -678,6 +714,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
enabledAutoRule.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = enabledAutoRule;
@@ -842,6 +879,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
disabledAutoRule.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = disabledAutoRule;
@@ -877,6 +915,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
automaticRules.put("customRule", customRule);
ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -886,6 +925,7 @@
defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultScheduleRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
@@ -922,6 +962,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
automaticRules.put("customRule", customRule);
ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -989,6 +1030,14 @@
assertEquals(1, mZenModeHelperSpy.mConditions.mSubscriptions.size());
}
+ @Test
+ public void testEmptyDefaultRulesMap() {
+ ZenModeConfig config = new ZenModeConfig();
+ config.automaticRules = new ArrayMap<>();
+ mZenModeHelperSpy.mConfig = config;
+ mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
+ }
+
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
@@ -1015,4 +1064,294 @@
assertTrue(mZenModeHelperSpy.mConfig.allowRepeatCallers);
assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
}
+
+ /**
+ * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml()
+ */
+ final class XmlResourceParserImpl implements XmlResourceParser {
+ private XmlPullParser parser;
+
+ public XmlResourceParserImpl(XmlPullParser parser) {
+ this.parser = parser;
+ }
+
+ public int getEventType() throws XmlPullParserException {
+ return parser.getEventType();
+ }
+
+ @Override
+ public void setFeature(String name, boolean state) throws XmlPullParserException {
+ parser.setFeature(name, state);
+ }
+
+ @Override
+ public boolean getFeature(String name) {
+ return false;
+ }
+
+ @Override
+ public void setProperty(String name, Object value) throws XmlPullParserException {
+ parser.setProperty(name, value);
+ }
+
+ @Override
+ public Object getProperty(String name) {
+ return parser.getProperty(name);
+ }
+
+ @Override
+ public void setInput(Reader in) throws XmlPullParserException {
+ parser.setInput(in);
+ }
+
+ @Override
+ public void setInput(InputStream inputStream, String inputEncoding)
+ throws XmlPullParserException {
+ parser.setInput(inputStream, inputEncoding);
+ }
+
+ @Override
+ public String getInputEncoding() {
+ return parser.getInputEncoding();
+ }
+
+ @Override
+ public void defineEntityReplacementText(String entityName, String replacementText)
+ throws XmlPullParserException {
+ parser.defineEntityReplacementText(entityName, replacementText);
+ }
+
+ @Override
+ public int getNamespaceCount(int depth) throws XmlPullParserException {
+ return parser.getNamespaceCount(depth);
+ }
+
+ @Override
+ public String getNamespacePrefix(int pos) throws XmlPullParserException {
+ return parser.getNamespacePrefix(pos);
+ }
+
+ @Override
+ public String getNamespaceUri(int pos) throws XmlPullParserException {
+ return parser.getNamespaceUri(pos);
+ }
+
+ @Override
+ public String getNamespace(String prefix) {
+ return parser.getNamespace(prefix);
+ }
+
+ @Override
+ public int getDepth() {
+ return parser.getDepth();
+ }
+
+ @Override
+ public String getPositionDescription() {
+ return parser.getPositionDescription();
+ }
+
+ @Override
+ public int getLineNumber() {
+ return parser.getLineNumber();
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return parser.getColumnNumber();
+ }
+
+ @Override
+ public boolean isWhitespace() throws XmlPullParserException {
+ return parser.isWhitespace();
+ }
+
+ @Override
+ public String getText() {
+ return parser.getText();
+ }
+
+ @Override
+ public char[] getTextCharacters(int[] holderForStartAndLength) {
+ return parser.getTextCharacters(holderForStartAndLength);
+ }
+
+ @Override
+ public String getNamespace() {
+ return parser.getNamespace();
+ }
+
+ @Override
+ public String getName() {
+ return parser.getName();
+ }
+
+ @Override
+ public String getPrefix() {
+ return parser.getPrefix();
+ }
+
+ @Override
+ public boolean isEmptyElementTag() throws XmlPullParserException {
+ return false;
+ }
+
+ @Override
+ public int getAttributeCount() {
+ return parser.getAttributeCount();
+ }
+
+ public int next() throws IOException, XmlPullParserException {
+ return parser.next();
+ }
+
+ @Override
+ public int nextToken() throws XmlPullParserException, IOException {
+ return parser.next();
+ }
+
+ @Override
+ public void require(int type, String namespace, String name)
+ throws XmlPullParserException, IOException {
+ parser.require(type, namespace, name);
+ }
+
+ @Override
+ public String nextText() throws XmlPullParserException, IOException {
+ return parser.nextText();
+ }
+
+ @Override
+ public String getAttributeNamespace(int index) {
+ return "";
+ }
+
+ @Override
+ public String getAttributeName(int index) {
+ return parser.getAttributeName(index);
+ }
+
+ @Override
+ public String getAttributePrefix(int index) {
+ return parser.getAttributePrefix(index);
+ }
+
+ @Override
+ public String getAttributeType(int index) {
+ return parser.getAttributeType(index);
+ }
+
+ @Override
+ public boolean isAttributeDefault(int index) {
+ return parser.isAttributeDefault(index);
+ }
+
+ @Override
+ public String getAttributeValue(int index) {
+ return parser.getAttributeValue(index);
+ }
+
+ @Override
+ public String getAttributeValue(String namespace, String name) {
+ return parser.getAttributeValue(namespace, name);
+ }
+
+ @Override
+ public int getAttributeNameResource(int index) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeListValue(String namespace, String attribute, String[] options,
+ int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getAttributeBooleanValue(String namespace, String attribute,
+ boolean defaultValue) {
+ return false;
+ }
+
+ @Override
+ public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeUnsignedIntValue(String namespace, String attribute,
+ int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public float getAttributeFloatValue(String namespace, String attribute,
+ float defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeListValue(int index, String[] options, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+ return false;
+ }
+
+ @Override
+ public int getAttributeResourceValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeIntValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public float getAttributeFloatValue(int index, float defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public String getIdAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getClassAttribute() {
+ return null;
+ }
+
+ @Override
+ public int getIdAttributeResourceValue(int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getStyleAttribute() {
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public int nextTag() throws IOException, XmlPullParserException {
+ return parser.nextTag();
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
new file mode 100644
index 0000000..03fb123
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
@@ -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.
+ */
+
+package com.android.server.policy;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.policy.
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyPolicyTests {
+
+ @Presubmit
+ @Test
+ public void preSubmitTest() {}
+
+ @FlakyTest
+ @Presubmit
+ @Test
+ public void flakyPreSubmitTest() {}
+
+ @Test
+ public void postSubmitTest() {}
+
+ @FlakyTest
+ @Test
+ public void flakyPostSubmitTest() {}
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 4b7e21f..db9972f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -24,7 +24,9 @@
import android.content.res.Configuration;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.proto.ProtoInputStream;
+import java.io.IOException;
import java.util.List;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,7 +48,7 @@
// keep hundreds of strings that have the same contents. We will read the string
// and only keep it if it's not in the cache. The GC will take care of the
// strings that had identical copies in the cache.
- private final ArraySet<String> mStringCache = new ArraySet<>();
+ public final ArraySet<String> mStringCache = new ArraySet<>();
public static final class EventTracker {
public long curStartTime;
@@ -129,6 +131,90 @@
return event;
}
+ /**
+ * Builds a UsageEvents.Event from a proto, but does not add it internally.
+ * Built here to take advantage of the cached String Refs
+ */
+ UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool)
+ throws IOException {
+ final UsageEvents.Event event = new UsageEvents.Event();
+ while (true) {
+ switch (parser.nextField()) {
+ case (int) IntervalStatsProto.Event.PACKAGE:
+ event.mPackage = getCachedStringRef(
+ parser.readString(IntervalStatsProto.Event.PACKAGE));
+ break;
+ case (int) IntervalStatsProto.Event.PACKAGE_INDEX:
+ event.mPackage = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.PACKAGE_INDEX) - 1));
+ break;
+ case (int) IntervalStatsProto.Event.CLASS:
+ event.mClass = getCachedStringRef(
+ parser.readString(IntervalStatsProto.Event.CLASS));
+ break;
+ case (int) IntervalStatsProto.Event.CLASS_INDEX:
+ event.mClass = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.CLASS_INDEX) - 1));
+ break;
+ case (int) IntervalStatsProto.Event.TIME_MS:
+ event.mTimeStamp = beginTime + parser.readLong(
+ IntervalStatsProto.Event.TIME_MS);
+ break;
+ case (int) IntervalStatsProto.Event.FLAGS:
+ event.mFlags = parser.readInt(IntervalStatsProto.Event.FLAGS);
+ break;
+ case (int) IntervalStatsProto.Event.TYPE:
+ event.mEventType = parser.readInt(IntervalStatsProto.Event.TYPE);
+ break;
+ case (int) IntervalStatsProto.Event.CONFIG:
+ event.mConfiguration = new Configuration();
+ event.mConfiguration.readFromProto(parser, IntervalStatsProto.Event.CONFIG);
+ break;
+ case (int) IntervalStatsProto.Event.SHORTCUT_ID:
+ event.mShortcutId = parser.readString(
+ IntervalStatsProto.Event.SHORTCUT_ID).intern();
+ break;
+ case (int) IntervalStatsProto.Event.STANDBY_BUCKET:
+ event.mBucketAndReason = parser.readInt(
+ IntervalStatsProto.Event.STANDBY_BUCKET);
+ break;
+ case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL:
+ event.mNotificationChannelId = parser.readString(
+ IntervalStatsProto.Event.NOTIFICATION_CHANNEL);
+ break;
+ case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX:
+ event.mNotificationChannelId = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX)
+ - 1));
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // Handle default values for certain events types
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration == null) {
+ event.mConfiguration = new Configuration();
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId == null) {
+ event.mShortcutId = "";
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId == null) {
+ event.mNotificationChannelId = "";
+ }
+ break;
+ }
+ if (event.mTimeStamp == 0) {
+ //mTimestamp not set, assume default value 0 plus beginTime
+ event.mTimeStamp = beginTime;
+ }
+ return event;
+ }
+ }
+ }
+
private boolean isStatefulEvent(int eventType) {
switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND:
@@ -143,8 +229,6 @@
/**
* Returns whether the event type is one caused by user visible
* interaction. Excludes those that are internally generated.
- * @param eventType
- * @return
*/
private boolean isUserVisibleEvent(int eventType) {
return eventType != UsageEvents.Event.SYSTEM_INTERACTION
@@ -184,6 +268,25 @@
endTime = timeStamp;
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void addEvent(UsageEvents.Event event) {
+ if (events == null) {
+ events = new EventList();
+ }
+ // Cache common use strings
+ event.mPackage = getCachedStringRef(event.mPackage);
+ if (event.mClass != null) {
+ event.mClass = getCachedStringRef(event.mClass);
+ }
+ if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
+ }
+ events.insert(event);
+ }
+
void updateChooserCounts(String packageName, String category, String action) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
if (usageStats.mChooserCounts == null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 5ab5dc2..c616685 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,6 +17,7 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
@@ -25,6 +26,10 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.io.IoUtils;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
@@ -32,18 +37,49 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
+import java.io.InputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
/**
- * Provides an interface to query for UsageStat data from an XML database.
+ * Provides an interface to query for UsageStat data from a Protocol Buffer database.
+ *
+ * Prior to version 4, UsageStatsDatabase used XML to store Usage Stats data to disk.
+ * When the UsageStatsDatabase version is upgraded, the files on disk are migrated to the new
+ * version on init. The steps of migration are as follows:
+ * 1) Check if version upgrade breadcrumb exists on disk, if so skip to step 4.
+ * 2) Copy current files to versioned backup files.
+ * 3) Write a temporary breadcrumb file with some info about the backed up files.
+ * 4) Deserialize a versioned backup file using the info written to the breadcrumb for the
+ * correct deserialization methodology.
+ * 5) Reserialize the data read from the file with the new version format and replace the old files
+ * 6) Repeat Step 3 and 4 for each versioned backup file matching the breadcrumb file.
+ * 7) Update the version file with the new version and build fingerprint.
+ * 8) Delete the versioned backup files (unless flagged to be kept).
+ * 9) Delete the breadcrumb file.
+ *
+ * Performing the upgrade steps in this order, protects against unexpected shutdowns mid upgrade
+ *
+ * A versioned backup file is simply a copy of a Usage Stats file with some extra info embedded in
+ * the file name. The structure of the versioned backup filename is as followed:
+ * (original file name).(backup timestamp).(original file version).vak
+ *
+ * During the version upgrade process, the new upgraded file will have it's name set to the original
+ * file name. The backup timestamp helps distinguish between versioned backups if multiple upgrades
+ * and downgrades have taken place. The original file version denotes how to parse the file.
*/
public class UsageStatsDatabase {
- private static final int CURRENT_VERSION = 3;
+ private static final int DEFAULT_CURRENT_VERSION = 4;
// Current version of the backup schema
static final int BACKUP_VERSION = 1;
@@ -52,10 +88,16 @@
// same as UsageStatsBackupHelper.KEY_USAGE_STATS
static final String KEY_USAGE_STATS = "usage_stats";
+ // Persist versioned backup files.
+ // Should be false, except when testing new versions
+ // STOPSHIP: b/111422946 this should be false on launch
+ static final boolean KEEP_VAK_FILES = true;
private static final String TAG = "UsageStatsDatabase";
- private static final boolean DEBUG = UsageStatsService.DEBUG;
+ // STOPSHIP: b/111422946 this should be boolean DEBUG = UsageStatsService.DEBUG; on launch
+ private static final boolean DEBUG = true;
private static final String BAK_SUFFIX = ".bak";
+ private static final String VERSIONED_BAK_SUFFIX = ".vak";
private static final String CHECKED_IN_SUFFIX = UsageStatsXml.CHECKED_IN_SUFFIX;
private static final String RETENTION_LEN_KEY = "ro.usagestats.chooser.retention";
private static final int SELECTION_LOG_RETENTION_LEN =
@@ -66,21 +108,40 @@
private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
private final UnixCalendar mCal;
private final File mVersionFile;
+ // If this file exists on disk, UsageStatsDatabase is in the middle of migrating files to a new
+ // version. If this file exists on boot, the upgrade was interrupted and needs to be picked up
+ // where it left off.
+ private final File mUpdateBreadcrumb;
+ // Current version of the database files schema
+ private final int mCurrentVersion;
private boolean mFirstUpdate;
private boolean mNewUpdate;
- public UsageStatsDatabase(File dir) {
- mIntervalDirs = new File[] {
+ /**
+ * UsageStatsDatabase constructor that allows setting the version number.
+ * This should only be used for testing.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public UsageStatsDatabase(File dir, int version) {
+ mIntervalDirs = new File[]{
new File(dir, "daily"),
new File(dir, "weekly"),
new File(dir, "monthly"),
new File(dir, "yearly"),
};
+ mCurrentVersion = version;
mVersionFile = new File(dir, "version");
+ mUpdateBreadcrumb = new File(dir, "breadcrumb");
mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
mCal = new UnixCalendar(0);
}
+ public UsageStatsDatabase(File dir) {
+ this(dir, DEFAULT_CURRENT_VERSION);
+ }
+
/**
* Initialize any directories required and index what stats are available.
*/
@@ -154,7 +215,7 @@
try {
IntervalStats stats = new IntervalStats();
for (int i = start; i < fileCount - 1; i++) {
- UsageStatsXml.read(files.valueAt(i), stats);
+ readLocked(files.valueAt(i), stats);
if (!checkinAction.checkin(stats)) {
return false;
}
@@ -190,7 +251,7 @@
final FilenameFilter backupFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return !name.endsWith(BAK_SUFFIX);
+ return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
}
};
@@ -210,7 +271,7 @@
for (File f : files) {
final AtomicFile af = new AtomicFile(f);
try {
- mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
+ mSortedStatFiles[i].put(parseBeginTime(af), af);
} catch (IOException e) {
Slog.e(TAG, "failed to index file: " + f, e);
}
@@ -252,14 +313,32 @@
version = 0;
}
- if (version != CURRENT_VERSION) {
- Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
- doUpgradeLocked(version);
+ if (version != mCurrentVersion) {
+ Slog.i(TAG, "Upgrading from version " + version + " to " + mCurrentVersion);
+ if (!mUpdateBreadcrumb.exists()) {
+ doUpgradeLocked(version);
+ } else {
+ Slog.i(TAG, "Version upgrade breadcrumb found on disk! Continuing version upgrade");
+ }
+
+ if (mUpdateBreadcrumb.exists()) {
+ int previousVersion;
+ long token;
+ try (BufferedReader reader = new BufferedReader(
+ new FileReader(mUpdateBreadcrumb))) {
+ token = Long.parseLong(reader.readLine());
+ previousVersion = Integer.parseInt(reader.readLine());
+ } catch (NumberFormatException | IOException e) {
+ Slog.e(TAG, "Failed read version upgrade breadcrumb");
+ throw new RuntimeException(e);
+ }
+ continueUpgradeLocked(previousVersion, token);
+ }
}
- if (version != CURRENT_VERSION || mNewUpdate) {
+ if (version != mCurrentVersion || mNewUpdate) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
- writer.write(Integer.toString(CURRENT_VERSION));
+ writer.write(Integer.toString(mCurrentVersion));
writer.write("\n");
writer.write(currentFingerprint);
writer.write("\n");
@@ -269,6 +348,14 @@
throw new RuntimeException(e);
}
}
+
+ if (mUpdateBreadcrumb.exists()) {
+ // Files should be up to date with current version. Clear the version update breadcrumb
+ if (!KEEP_VAK_FILES) {
+ removeVersionedBackupFiles();
+ }
+ mUpdateBreadcrumb.delete();
+ }
}
private String getBuildFingerprint() {
@@ -290,6 +377,119 @@
}
}
}
+ } else {
+ // Turn all current usage stats files into versioned backup files
+ final long token = System.currentTimeMillis();
+ final FilenameFilter backupFileFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
+ }
+ };
+
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles(backupFileFilter);
+ if (files != null) {
+ for (int j = 0; j < files.length; j++) {
+ final File backupFile = new File(
+ files[j].toString() + "." + Long.toString(token) + "."
+ + Integer.toString(thisVersion) + VERSIONED_BAK_SUFFIX);
+ if (DEBUG) {
+ Slog.d(TAG, "Creating versioned (" + Integer.toString(thisVersion)
+ + ") backup of " + files[j].toString()
+ + " stat files for interval "
+ + i + " to " + backupFile.toString());
+ }
+
+ try {
+ // Backup file should not already exist, but make sure it doesn't
+ Files.deleteIfExists(backupFile.toPath());
+ Files.move(files[j].toPath(), backupFile.toPath(),
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to back up file : " + files[j].toString());
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ // Leave a breadcrumb behind noting that all the usage stats have been copied to a
+ // versioned backup.
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(mUpdateBreadcrumb));
+ writer.write(Long.toString(token));
+ writer.write("\n");
+ writer.write(Integer.toString(thisVersion));
+ writer.write("\n");
+ writer.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write new version upgrade breadcrumb");
+ throw new RuntimeException(e);
+ } finally {
+ IoUtils.closeQuietly(writer);
+ }
+ }
+ }
+
+ private void continueUpgradeLocked(int version, long token) {
+ // Read all the backed ups for the specified version and rewrite them with the current
+ // version's file format.
+ final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith("." + Long.toString(token) + "." + Integer.toString(version)
+ + VERSIONED_BAK_SUFFIX);
+ }
+ };
+
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+ if (files != null) {
+ for (int j = 0; j < files.length; j++) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Upgrading " + files[j].toString() + " to version ("
+ + Integer.toString(
+ mCurrentVersion) + ") for interval " + i);
+ }
+ try {
+ IntervalStats stats = new IntervalStats();
+ readLocked(new AtomicFile(files[j]), stats, version);
+ writeLocked(new AtomicFile(new File(mIntervalDirs[i],
+ Long.toString(stats.beginTime))), stats, mCurrentVersion);
+ } catch (IOException e) {
+ Slog.e(TAG,
+ "Failed to upgrade versioned backup file : " + files[j].toString());
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+ private void removeVersionedBackupFiles() {
+ final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(VERSIONED_BAK_SUFFIX);
+ }
+ };
+
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+ if (files != null) {
+ for (int j = 0; j < files.length; j++) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Removing " + files[j].toString() + " for interval " + i);
+ }
+ if (!files[j].delete()) {
+ Slog.e(TAG, "Failed to delete file : " + files[j].toString());
+ }
+ }
+ }
}
}
@@ -357,7 +557,7 @@
try {
final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1);
IntervalStats stats = new IntervalStats();
- UsageStatsXml.read(f, stats);
+ readLocked(f, stats);
return stats;
} catch (IOException e) {
Slog.e(TAG, "Failed to read usage stats file", e);
@@ -379,8 +579,8 @@
* which means you should make a copy of the data before adding it to the
* <code>accumulatedResult</code> list.
*
- * @param stats The {@link IntervalStats} object selected.
- * @param mutable Whether or not the data inside the stats object is mutable.
+ * @param stats The {@link IntervalStats} object selected.
+ * @param mutable Whether or not the data inside the stats object is mutable.
* @param accumulatedResult The list to which to add extracted data.
*/
void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
@@ -443,7 +643,7 @@
}
try {
- UsageStatsXml.read(f, stats);
+ readLocked(f, stats);
if (beginTime < stats.endTime) {
combiner.combine(stats, false, results);
}
@@ -523,14 +723,9 @@
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
- String path = f.getPath();
- if (path.endsWith(BAK_SUFFIX)) {
- f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
- }
-
long beginTime;
try {
- beginTime = UsageStatsXml.parseBeginTime(f);
+ beginTime = parseBeginTime(f);
} catch (IOException e) {
beginTime = 0;
}
@@ -542,18 +737,13 @@
}
}
- private static void pruneChooserCountsOlderThan(File dir, long expiryTime) {
+ private void pruneChooserCountsOlderThan(File dir, long expiryTime) {
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
- String path = f.getPath();
- if (path.endsWith(BAK_SUFFIX)) {
- f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
- }
-
long beginTime;
try {
- beginTime = UsageStatsXml.parseBeginTime(f);
+ beginTime = parseBeginTime(f);
} catch (IOException e) {
beginTime = 0;
}
@@ -562,7 +752,7 @@
try {
final AtomicFile af = new AtomicFile(f);
final IntervalStats stats = new IntervalStats();
- UsageStatsXml.read(af, stats);
+ readLocked(af, stats);
final int pkgCount = stats.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
UsageStats pkgStats = stats.packageStats.valueAt(i);
@@ -570,7 +760,7 @@
pkgStats.mChooserCounts.clear();
}
}
- UsageStatsXml.write(af, stats);
+ writeLocked(af, stats);
} catch (IOException e) {
Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
}
@@ -579,6 +769,222 @@
}
}
+
+ private static long parseBeginTime(AtomicFile file) throws IOException {
+ return parseBeginTime(file.getBaseFile());
+ }
+
+ private static long parseBeginTime(File file) throws IOException {
+ String name = file.getName();
+
+ // Parse out the digits from the the front of the file name
+ for (int i = 0; i < name.length(); i++) {
+ final char c = name.charAt(i);
+ if (c < '0' || c > '9') {
+ // found first char that is not a digit.
+ name = name.substring(0, i);
+ break;
+ }
+ }
+
+ try {
+ return Long.parseLong(name);
+ } catch (NumberFormatException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
+ writeLocked(file, stats, mCurrentVersion);
+ }
+
+ private static void writeLocked(AtomicFile file, IntervalStats stats, int version)
+ throws IOException {
+ FileOutputStream fos = file.startWrite();
+ try {
+ writeLocked(fos, stats, version);
+ file.finishWrite(fos);
+ fos = null;
+ } finally {
+ // When fos is null (successful write), this will no-op
+ file.failWrite(fos);
+ }
+ }
+
+ private void writeLocked(OutputStream out, IntervalStats stats) throws IOException {
+ writeLocked(out, stats, mCurrentVersion);
+ }
+
+ private static void writeLocked(OutputStream out, IntervalStats stats, int version)
+ throws IOException {
+ switch (version) {
+ case 1:
+ case 2:
+ case 3:
+ UsageStatsXml.write(out, stats);
+ break;
+ case 4:
+ UsageStatsProto.write(out, stats);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+ + " on write.");
+ }
+ }
+
+ private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
+ readLocked(file, statsOut, mCurrentVersion);
+ }
+
+ private static void readLocked(AtomicFile file, IntervalStats statsOut, int version)
+ throws IOException {
+ try {
+ FileInputStream in = file.openRead();
+ try {
+ statsOut.beginTime = parseBeginTime(file);
+ readLocked(in, statsOut, version);
+ statsOut.lastTimeSaved = file.getLastModifiedTime();
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Empty
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "UsageStatsDatabase", e);
+ throw e;
+ }
+ // STOPSHIP: b/111422946, b/115429334
+ // Everything below this comment is sanity check against the new database version.
+ // After the new version has soaked for some time the following should removed.
+ // The goal of this check is to make sure the the ProtoInputStream is properly reading from
+ // the UsageStats files.
+ final StringBuilder sb = new StringBuilder();
+ final int failureLogLimit = 10;
+ int failures = 0;
+
+ final int packagesSize = statsOut.packageStats.size();
+ for (int i = 0; i < packagesSize; i++) {
+ final UsageStats stat = statsOut.packageStats.valueAt(i);
+ if (stat == null) {
+ // ArrayMap may contain null values, skip them
+ continue;
+ }
+ if (stat.mPackageName.isEmpty()) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected empty usage stats package name loaded");
+ }
+ }
+ if (stat.mBeginTimeStamp > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnreasonable usage stats stat begin timestamp ");
+ sb.append(stat.mBeginTimeStamp);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ if (stat.mEndTimeStamp > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnreasonable usage stats stat end timestamp ");
+ sb.append(stat.mEndTimeStamp);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ if (stat.mLastTimeUsed > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnreasonable usage stats stat last used timestamp ");
+ sb.append(stat.mLastTimeUsed);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ }
+
+ if (statsOut.events != null) {
+ final int eventSize = statsOut.events.size();
+ for (int i = 0; i < eventSize; i++) {
+ final UsageEvents.Event event = statsOut.events.get(i);
+ if (event.mPackage.isEmpty()) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected empty empty package name loaded");
+ }
+ }
+ if (event.mTimeStamp < statsOut.beginTime || event.mTimeStamp > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected event timestamp ");
+ sb.append(event.mTimeStamp);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ if (event.mEventType < 0 || event.mEventType > UsageEvents.Event.MAX_EVENT_TYPE) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected event type ");
+ sb.append(event.mEventType);
+ sb.append(" loaded");
+ }
+ }
+ if ((event.mFlags & ~UsageEvents.Event.VALID_FLAG_BITS) != 0) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected event flag bit 0b");
+ sb.append(Integer.toBinaryString(event.mFlags));
+ sb.append(" loaded");
+ }
+ }
+ }
+ }
+
+ if (failures != 0) {
+ if (failures > failureLogLimit) {
+ sb.append("\nFailure log limited (");
+ sb.append(failures);
+ sb.append(" total failures found!)");
+ }
+ sb.append("\nError found in:\n");
+ sb.append(file.getBaseFile().getAbsolutePath());
+ sb.append("\nPlease go to b/115429334 to help root cause this issue");
+ Slog.wtf(TAG,sb.toString());
+ }
+ }
+
+ private void readLocked(InputStream in, IntervalStats statsOut) throws IOException {
+ readLocked(in, statsOut, mCurrentVersion);
+ }
+
+ private static void readLocked(InputStream in, IntervalStats statsOut, int version)
+ throws IOException {
+ switch (version) {
+ case 1:
+ case 2:
+ case 3:
+ UsageStatsXml.read(in, statsOut);
+ break;
+ case 4:
+ UsageStatsProto.read(in, statsOut);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+ + " on read.");
+ }
+
+ }
+
/**
* Update the stats in the database. They may not be written to disk immediately.
*/
@@ -596,7 +1002,7 @@
mSortedStatFiles[intervalType].put(stats.beginTime, f);
}
- UsageStatsXml.write(f, stats);
+ writeLocked(f, stats);
stats.lastTimeSaved = f.getLastModifiedTime();
}
}
@@ -730,7 +1136,7 @@
throws IOException {
IntervalStats stats = new IntervalStats();
try {
- UsageStatsXml.read(statsFile, stats);
+ readLocked(statsFile, stats);
} catch (IOException e) {
Slog.e(TAG, "Failed to read usage stats file", e);
out.writeInt(0);
@@ -756,12 +1162,12 @@
if (stats.events != null) stats.events.clear();
}
- private static byte[] serializeIntervalStats(IntervalStats stats) {
+ private byte[] serializeIntervalStats(IntervalStats stats) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
try {
out.writeLong(stats.beginTime);
- UsageStatsXml.write(out, stats);
+ writeLocked(out, stats);
} catch (IOException ioe) {
Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
baos.reset();
@@ -769,13 +1175,13 @@
return baos.toByteArray();
}
- private static IntervalStats deserializeIntervalStats(byte[] data) {
+ private IntervalStats deserializeIntervalStats(byte[] data) {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream in = new DataInputStream(bais);
IntervalStats stats = new IntervalStats();
try {
stats.beginTime = in.readLong();
- UsageStatsXml.read(in, stats);
+ readLocked(in, stats);
} catch (IOException ioe) {
Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
stats = null;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
new file mode 100644
index 0000000..30d303f
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.usage;
+
+import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.content.res.Configuration;
+import android.util.ArrayMap;
+
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UsageStats reader/writer for Protocol Buffer format
+ */
+final class UsageStatsProto {
+ private static String TAG = "UsageStatsProto";
+
+ // Static-only utility class.
+ private UsageStatsProto() {}
+
+ private static List<String> readStringPool(ProtoInputStream proto) throws IOException {
+
+ final long token = proto.start(IntervalStatsProto.STRINGPOOL);
+ List<String> stringPool;
+ if (proto.isNextField(IntervalStatsProto.StringPool.SIZE)) {
+ stringPool = new ArrayList(proto.readInt(IntervalStatsProto.StringPool.SIZE));
+ } else {
+ stringPool = new ArrayList();
+ }
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) IntervalStatsProto.StringPool.STRINGS:
+ stringPool.add(proto.readString(IntervalStatsProto.StringPool.STRINGS));
+ break;
+ }
+ }
+ proto.end(token);
+ return stringPool;
+ }
+
+ private static void loadUsageStats(ProtoInputStream proto, long fieldId,
+ IntervalStats statsOut, List<String> stringPool)
+ throws IOException {
+
+ final long token = proto.start(fieldId);
+ UsageStats stats;
+ if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE_INDEX)) {
+ // Fast path reading the package name index. Most cases this should work since it is
+ // written first
+ stats = statsOut.getOrCreateUsageStats(
+ stringPool.get(proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1));
+ } else if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE)) {
+ // No package index, try package name instead
+ stats = statsOut.getOrCreateUsageStats(
+ proto.readString(IntervalStatsProto.UsageStats.PACKAGE));
+ } else {
+ // Temporarily store collected data to a UsageStats object. This is not efficient.
+ stats = new UsageStats();
+ }
+
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) IntervalStatsProto.UsageStats.PACKAGE:
+ // Fast track failed from some reason, add UsageStats object to statsOut now
+ UsageStats tempPackage = statsOut.getOrCreateUsageStats(
+ proto.readString(IntervalStatsProto.UsageStats.PACKAGE));
+ tempPackage.mLastTimeUsed = stats.mLastTimeUsed;
+ tempPackage.mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ tempPackage.mLastEvent = stats.mLastEvent;
+ tempPackage.mAppLaunchCount = stats.mAppLaunchCount;
+ stats = tempPackage;
+ break;
+ case (int) IntervalStatsProto.UsageStats.PACKAGE_INDEX:
+ // Fast track failed from some reason, add UsageStats object to statsOut now
+ UsageStats tempPackageIndex = statsOut.getOrCreateUsageStats(stringPool.get(
+ proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1));
+ tempPackageIndex.mLastTimeUsed = stats.mLastTimeUsed;
+ tempPackageIndex.mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ tempPackageIndex.mLastEvent = stats.mLastEvent;
+ tempPackageIndex.mAppLaunchCount = stats.mAppLaunchCount;
+ stats = tempPackageIndex;
+ break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS:
+ stats.mLastTimeUsed = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS:
+ stats.mTotalTimeInForeground = proto.readLong(
+ IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.LAST_EVENT:
+ stats.mLastEvent = proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT);
+ break;
+ case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT:
+ stats.mAppLaunchCount = proto.readInt(
+ IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT);
+ break;
+ case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS:
+ final long chooserToken = proto.start(
+ IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+ loadChooserCounts(proto, stats);
+ proto.end(chooserToken);
+ break;
+ }
+ }
+ if (stats.mLastTimeUsed == 0) {
+ // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime;
+ stats.mLastTimeUsed = statsOut.beginTime;
+ }
+ proto.end(token);
+ }
+
+ private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
+ IntervalStats.EventTracker tracker) throws IOException {
+ final long token = proto.start(fieldId);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.CountAndTime.COUNT:
+ tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
+ break;
+ case (int) IntervalStatsProto.CountAndTime.TIME_MS:
+ tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ proto.end(token);
+ return;
+ }
+ }
+ }
+
+ private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats)
+ throws IOException {
+ if (usageStats.mChooserCounts == null) {
+ usageStats.mChooserCounts = new ArrayMap<>();
+ }
+ String action = null;
+ ArrayMap<String, Integer> counts;
+ if (proto.isNextField(IntervalStatsProto.UsageStats.ChooserAction.NAME)) {
+ // Fast path reading the action name. Most cases this should work since it is written
+ // first
+ action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME);
+ counts = usageStats.mChooserCounts.get(action);
+ if (counts == null) {
+ counts = new ArrayMap<>();
+ usageStats.mChooserCounts.put(action, counts);
+ }
+ } else {
+ // Temporarily store collected data to an ArrayMap. This is not efficient.
+ counts = new ArrayMap<>();
+ }
+
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.NAME:
+ // Fast path failed from some reason, add the ArrayMap object to usageStats now
+ action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME);
+ usageStats.mChooserCounts.put(action, counts);
+ break;
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.COUNTS:
+ final long token = proto.start(
+ IntervalStatsProto.UsageStats.ChooserAction.COUNTS);
+ loadCountsForAction(proto, counts);
+ proto.end(token);
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (action == null) {
+ // default string
+ usageStats.mChooserCounts.put("", counts);
+ }
+ return;
+ }
+ }
+ }
+
+ private static void loadCountsForAction(ProtoInputStream proto,
+ ArrayMap<String, Integer> counts) throws IOException {
+ String category = null;
+ int count = 0;
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME:
+ category = proto.readString(
+ IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME);
+ break;
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT:
+ count = proto.readInt(
+ IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (category == null) {
+ counts.put("", count);
+ } else {
+ counts.put(category, count);
+ }
+ return;
+ }
+ }
+ }
+
+ private static void loadConfigStats(ProtoInputStream proto, long fieldId,
+ IntervalStats statsOut) throws IOException {
+ final long token = proto.start(fieldId);
+ boolean configActive = false;
+ final Configuration config = new Configuration();
+ ConfigurationStats configStats;
+ if (proto.isNextField(IntervalStatsProto.Configuration.CONFIG)) {
+ // Fast path reading the configuration. Most cases this should work since it is
+ // written first
+ config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG);
+ configStats = statsOut.getOrCreateConfigurationStats(config);
+ } else {
+ // Temporarily store collected data to a ConfigurationStats object. This is not
+ // efficient.
+ configStats = new ConfigurationStats();
+ }
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.Configuration.CONFIG:
+ // Fast path failed from some reason, add ConfigStats object to statsOut now
+ config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG);
+ final ConfigurationStats temp = statsOut.getOrCreateConfigurationStats(config);
+ temp.mLastTimeActive = configStats.mLastTimeActive;
+ temp.mTotalTimeActive = configStats.mTotalTimeActive;
+ temp.mActivationCount = configStats.mActivationCount;
+ configStats = temp;
+ break;
+ case (int) IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS:
+ configStats.mLastTimeActive = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS:
+ configStats.mTotalTimeActive = proto.readLong(
+ IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.Configuration.COUNT:
+ configStats.mActivationCount = proto.readInt(
+ IntervalStatsProto.Configuration.COUNT);
+ break;
+ case (int) IntervalStatsProto.Configuration.ACTIVE:
+ configActive = proto.readBoolean(IntervalStatsProto.Configuration.ACTIVE);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (configStats.mLastTimeActive == 0) {
+ //mLastTimeActive was not assigned, assume default value of 0 plus beginTime
+ configStats.mLastTimeActive = statsOut.beginTime;
+ }
+ if (configActive) {
+ statsOut.activeConfiguration = configStats.mConfiguration;
+ }
+ proto.end(token);
+ return;
+ }
+ }
+ }
+
+ private static void loadEvent(ProtoInputStream proto, long fieldId, IntervalStats statsOut,
+ List<String> stringPool) throws IOException {
+ final long token = proto.start(fieldId);
+ UsageEvents.Event event = statsOut.buildEvent(proto, stringPool);
+ proto.end(token);
+ if (event.mPackage == null) {
+ throw new ProtocolException("no package field present");
+ }
+
+ if (statsOut.events == null) {
+ statsOut.events = new EventList();
+ }
+ statsOut.events.insert(event);
+ }
+
+ private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats)
+ throws IOException {
+ final long token = proto.start(IntervalStatsProto.STRINGPOOL);
+ final int size = stats.mStringCache.size();
+ proto.write(IntervalStatsProto.StringPool.SIZE, size);
+ for (int i = 0; i < size; i++) {
+ proto.write(IntervalStatsProto.StringPool.STRINGS, stats.mStringCache.valueAt(i));
+ }
+ proto.end(token);
+ }
+
+ private static void writeUsageStats(ProtoOutputStream proto, long fieldId,
+ final IntervalStats stats, final UsageStats usageStats) throws IOException {
+ final long token = proto.start(fieldId);
+ // Write the package name first, so loadUsageStats can avoid creating an extra object
+ final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName);
+ if (packageIndex >= 0) {
+ proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1);
+ } else {
+ // Package not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
+ }
+ proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS,
+ usageStats.mLastTimeUsed - stats.beginTime);
+ proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS,
+ usageStats.mTotalTimeInForeground);
+ proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, usageStats.mLastEvent);
+ proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
+ writeChooserCounts(proto, usageStats);
+ proto.end(token);
+ }
+
+ private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
+ long time) throws IOException {
+ final long token = proto.start(fieldId);
+ proto.write(IntervalStatsProto.CountAndTime.COUNT, count);
+ proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time);
+ proto.end(token);
+ }
+
+
+ private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats)
+ throws IOException {
+ if (usageStats == null || usageStats.mChooserCounts == null
+ || usageStats.mChooserCounts.keySet().isEmpty()) {
+ return;
+ }
+ final int chooserCountSize = usageStats.mChooserCounts.size();
+ for (int i = 0; i < chooserCountSize; i++) {
+ final String action = usageStats.mChooserCounts.keyAt(i);
+ final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+ if (action == null || counts == null || counts.isEmpty()) {
+ continue;
+ }
+ final long token = proto.start(IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+ proto.write(IntervalStatsProto.UsageStats.ChooserAction.NAME, action);
+ writeCountsForAction(proto, counts);
+ proto.end(token);
+ }
+ }
+
+ private static void writeCountsForAction(ProtoOutputStream proto,
+ ArrayMap<String, Integer> counts) throws IOException {
+ final int countsSize = counts.size();
+ for (int i = 0; i < countsSize; i++) {
+ String key = counts.keyAt(i);
+ int count = counts.valueAt(i);
+ if (count > 0) {
+ final long token = proto.start(IntervalStatsProto.UsageStats.ChooserAction.COUNTS);
+ proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME, key);
+ proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT, count);
+ proto.end(token);
+ }
+ }
+ }
+
+ private static void writeConfigStats(ProtoOutputStream proto, long fieldId,
+ final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)
+ throws IOException {
+ final long token = proto.start(fieldId);
+ configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG);
+ proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
+ configStats.mLastTimeActive - stats.beginTime);
+ proto.write(IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS,
+ configStats.mTotalTimeActive);
+ proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount);
+ proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive);
+ proto.end(token);
+
+ }
+
+ private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats,
+ final UsageEvents.Event event) throws IOException {
+ final long token = proto.start(fieldId);
+ final int packageIndex = stats.mStringCache.indexOf(event.mPackage);
+ if (packageIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1);
+ } else {
+ // Package not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "Usage event package name (" + event.mPackage
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage);
+ }
+ if (event.mClass != null) {
+ final int classIndex = stats.mStringCache.indexOf(event.mClass);
+ if (classIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1);
+ } else {
+ // Class not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "Usage event class name (" + event.mClass
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
+ }
+ }
+ proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
+ proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
+ proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration != null) {
+ event.mConfiguration.writeToProto(proto, IntervalStatsProto.Event.CONFIG);
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId != null) {
+ proto.write(IntervalStatsProto.Event.SHORTCUT_ID, event.mShortcutId);
+ }
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ if (event.mBucketAndReason != 0) {
+ proto.write(IntervalStatsProto.Event.STANDBY_BUCKET, event.mBucketAndReason);
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId != null) {
+ final int channelIndex = stats.mStringCache.indexOf(
+ event.mNotificationChannelId);
+ if (channelIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX,
+ channelIndex + 1);
+ } else {
+ // Channel not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "Usage event notification channel name ("
+ + event.mNotificationChannelId
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL,
+ event.mNotificationChannelId);
+ }
+ }
+ break;
+ }
+ proto.end(token);
+ }
+
+ /**
+ * Reads from the {@link ProtoInputStream}.
+ *
+ * @param proto The proto from which to read events.
+ * @param statsOut The stats object to populate with the data from the XML file.
+ */
+ public static void read(InputStream in, IntervalStats statsOut) throws IOException {
+ final ProtoInputStream proto = new ProtoInputStream(in);
+ List<String> stringPool = null;
+
+ statsOut.packageStats.clear();
+ statsOut.configurations.clear();
+ statsOut.activeConfiguration = null;
+
+ if (statsOut.events != null) {
+ statsOut.events.clear();
+ }
+
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.END_TIME_MS:
+ statsOut.endTime = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.END_TIME_MS);
+ break;
+ case (int) IntervalStatsProto.INTERACTIVE:
+ loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE,
+ statsOut.interactiveTracker);
+ break;
+ case (int) IntervalStatsProto.NON_INTERACTIVE:
+ loadCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+ statsOut.nonInteractiveTracker);
+ break;
+ case (int) IntervalStatsProto.KEYGUARD_SHOWN:
+ loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+ statsOut.keyguardShownTracker);
+ break;
+ case (int) IntervalStatsProto.KEYGUARD_HIDDEN:
+ loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+ statsOut.keyguardHiddenTracker);
+ break;
+ case (int) IntervalStatsProto.STRINGPOOL:
+ stringPool = readStringPool(proto);
+ statsOut.mStringCache.addAll(stringPool);
+ break;
+ case (int) IntervalStatsProto.PACKAGES:
+ loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+ break;
+ case (int) IntervalStatsProto.CONFIGURATIONS:
+ loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+ break;
+ case (int) IntervalStatsProto.EVENT_LOG:
+ loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (statsOut.endTime == 0) {
+ // endTime not assigned, assume default value of 0 plus beginTime
+ statsOut.endTime = statsOut.beginTime;
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Writes the stats object to an ProtoBuf file.
+ *
+ * @param proto The serializer to which to write the packageStats data.
+ * @param stats The stats object to write to the XML file.
+ */
+ public static void write(OutputStream out, IntervalStats stats) throws IOException {
+ final ProtoOutputStream proto = new ProtoOutputStream(out);
+ proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
+ // String pool should be written before the rest of the usage stats
+ writeStringPool(proto, stats);
+
+ writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
+ stats.interactiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+ stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+ stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+ stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+
+ final int statsCount = stats.packageStats.size();
+ for (int i = 0; i < statsCount; i++) {
+ writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
+ stats.packageStats.valueAt(i));
+ }
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+ writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
+ stats.configurations.valueAt(i), active);
+ }
+ final int eventCount = stats.events != null ? stats.events.size() : 0;
+ for (int i = 0; i < eventCount; i++) {
+ writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
+ }
+
+ proto.flush();
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java
index e7db741..f8d1113 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXml.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java
@@ -19,6 +19,9 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -33,61 +36,7 @@
private static final String VERSION_ATTR = "version";
static final String CHECKED_IN_SUFFIX = "-c";
- public static long parseBeginTime(AtomicFile file) throws IOException {
- return parseBeginTime(file.getBaseFile());
- }
-
- public static long parseBeginTime(File file) throws IOException {
- String name = file.getName();
-
- // Eat as many occurrences of -c as possible. This is due to a bug where -c
- // would be appended more than once to a checked-in file, causing a crash
- // on boot when indexing files since Long.parseLong() will puke on anything but
- // a number.
- while (name.endsWith(CHECKED_IN_SUFFIX)) {
- name = name.substring(0, name.length() - CHECKED_IN_SUFFIX.length());
- }
-
- try {
- return Long.parseLong(name);
- } catch (NumberFormatException e) {
- throw new IOException(e);
- }
- }
-
- public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
- try {
- FileInputStream in = file.openRead();
- try {
- statsOut.beginTime = parseBeginTime(file);
- read(in, statsOut);
- statsOut.lastTimeSaved = file.getLastModifiedTime();
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- // Empty
- }
- }
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "UsageStats Xml", e);
- throw e;
- }
- }
-
- public static void write(AtomicFile file, IntervalStats stats) throws IOException {
- FileOutputStream fos = file.startWrite();
- try {
- write(fos, stats);
- file.finishWrite(fos);
- fos = null;
- } finally {
- // When fos is null (successful write), this will no-op
- file.failWrite(fos);
- }
- }
-
- static void read(InputStream in, IntervalStats statsOut) throws IOException {
+ public static void read(InputStream in, IntervalStats statsOut) throws IOException {
XmlPullParser parser = Xml.newPullParser();
try {
parser.setInput(in, "utf-8");
@@ -113,7 +62,7 @@
}
}
- static void write(OutputStream out, IntervalStats stats) throws IOException {
+ public static void write(OutputStream out, IntervalStats stats) throws IOException {
FastXmlSerializer xml = new FastXmlSerializer();
xml.setOutput(out, "utf-8");
xml.startDocument("utf-8", true);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 6a1e97a..a68f9d3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -196,11 +196,7 @@
event.mNotificationChannelId = (channelId != null) ? channelId.intern() : null;
break;
}
-
- if (statsOut.events == null) {
- statsOut.events = new EventList();
- }
- statsOut.events.insert(event);
+ statsOut.addEvent(event);
}
private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b194e9..1a8aba0 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -176,12 +176,8 @@
currentDailyStats.activeConfiguration, newFullConfig);
}
- // Add the event to the daily list.
- if (currentDailyStats.events == null) {
- currentDailyStats.events = new EventList();
- }
if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
- currentDailyStats.events.insert(event);
+ currentDailyStats.addEvent(event);
}
boolean incrementAppLaunch = false;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c5d6dc7..99ad1f4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,8 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
@@ -1128,6 +1130,27 @@
}
}
+ @Override
+ public void getActiveServiceSupportedActions(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+ synchronized (this) {
+ if (mImpl == null) {
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.getActiveServiceSupportedActions(voiceActions, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
public void onSessionShown() {
synchronized (this) {
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 57e9f66..61d7d6c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -25,6 +25,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
@@ -57,6 +59,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
final static String TAG = "VoiceInteractionServiceManager";
@@ -177,6 +180,23 @@
activityTokens);
}
+ public void getActiveServiceSupportedActions(List<String> commands,
+ IVoiceActionCheckCallback callback) {
+ if (mService == null) {
+ Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ try {
+ mService.getActiveServiceSupportedActions(commands, callback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e);
+ }
+ }
+
public boolean hideSessionLocked() {
if (mActiveSession != null) {
return mActiveSession.hideLocked();
diff --git a/startop/tools/view_compiler/Android.bp b/startop/tools/view_compiler/Android.bp
new file mode 100644
index 0000000..c3e9184
--- /dev/null
+++ b/startop/tools/view_compiler/Android.bp
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_host_static {
+ name: "libviewcompiler",
+ srcs: [
+ "java_lang_builder.cc",
+ "util.cc",
+ ],
+ static_libs: [
+ "libbase"
+ ]
+}
+
+cc_binary_host {
+ name: "viewcompiler",
+ srcs: [
+ "main.cc",
+ ],
+ static_libs: [
+ "libbase",
+ "libtinyxml2",
+ "libgflags",
+ "libviewcompiler",
+ ],
+}
+
+cc_test_host {
+ name: "view-compiler-tests",
+ srcs: [
+ "util_test.cc",
+ ],
+ static_libs: [
+ "libviewcompiler",
+ ]
+}
diff --git a/startop/tools/view_compiler/README.md b/startop/tools/view_compiler/README.md
new file mode 100644
index 0000000..5659501
--- /dev/null
+++ b/startop/tools/view_compiler/README.md
@@ -0,0 +1,25 @@
+# View Compiler
+
+This directory contains an experimental compiler for layout files.
+
+It will take a layout XML file and produce a CompiledLayout.java file with a
+specialized layout inflation function.
+
+To use it, let's assume you had a layout in `my_layout.xml` and your app was in
+the Java language package `com.example.myapp`. Run the following command:
+
+ viewcompiler my_layout.xml --package com.example.myapp --out CompiledView.java
+
+This will produce a `CompiledView.java`, which can then be compiled into your
+Android app. Then to use it, in places where you would have inflated
+`R.layouts.my_layout`, instead call `CompiledView.inflate`.
+
+Precompiling views like this generally improves the time needed to inflate them.
+
+This tool is still in its early stages and has a number of limitations.
+* Currently only one layout can be compiled at a time.
+* `merge` and `include` nodes are not supported.
+* View compilation is a manual process that requires code changes in the
+ application.
+* This only works for apps that do not use a custom layout inflater.
+* Other limitations yet to be discovered.
diff --git a/startop/tools/view_compiler/TEST_MAPPING b/startop/tools/view_compiler/TEST_MAPPING
new file mode 100644
index 0000000..cc4b17a
--- /dev/null
+++ b/startop/tools/view_compiler/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "view-compiler-tests"
+ }
+ ]
+}
diff --git a/startop/tools/view_compiler/java_lang_builder.cc b/startop/tools/view_compiler/java_lang_builder.cc
new file mode 100644
index 0000000..0b8754f
--- /dev/null
+++ b/startop/tools/view_compiler/java_lang_builder.cc
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "java_lang_builder.h"
+
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
+using std::string;
+
+void JavaLangViewBuilder::Start() const {
+ out_ << StringPrintf("package %s;\n", package_.c_str())
+ << "import android.content.Context;\n"
+ "import android.content.res.Resources;\n"
+ "import android.content.res.XmlResourceParser;\n"
+ "import android.util.AttributeSet;\n"
+ "import android.util.Xml;\n"
+ "import android.view.*;\n"
+ "import android.widget.*;\n"
+ "\n"
+ "public final class CompiledView {\n"
+ "\n"
+ "static <T extends View> T createView(Context context, AttributeSet attrs, View parent, "
+ "String name, LayoutInflater.Factory factory, LayoutInflater.Factory2 factory2) {"
+ "\n"
+ " if (factory2 != null) {\n"
+ " return (T)factory2.onCreateView(parent, name, context, attrs);\n"
+ " } else if (factory != null) {\n"
+ " return (T)factory.onCreateView(name, context, attrs);\n"
+ " }\n"
+ // TODO: find a way to call the private factory
+ " return null;\n"
+ "}\n"
+ "\n"
+ " public static View inflate(Context context) {\n"
+ " try {\n"
+ " LayoutInflater inflater = LayoutInflater.from(context);\n"
+ " LayoutInflater.Factory factory = inflater.getFactory();\n"
+ " LayoutInflater.Factory2 factory2 = inflater.getFactory2();\n"
+ " Resources res = context.getResources();\n"
+ << StringPrintf(" XmlResourceParser xml = res.getLayout(%s.R.layout.%s);\n",
+ package_.c_str(),
+ layout_name_.c_str())
+ << " AttributeSet attrs = Xml.asAttributeSet(xml);\n"
+ // The Java-language XmlPullParser needs a call to next to find the start document tag.
+ " xml.next(); // start document\n";
+}
+
+void JavaLangViewBuilder::Finish() const {
+ out_ << " } catch (Exception e) {\n"
+ " return null;\n"
+ " }\n" // end try
+ " }\n" // end inflate
+ "}\n"; // end CompiledView
+}
+
+void JavaLangViewBuilder::StartView(const string& class_name) {
+ const string view_var = MakeVar("view");
+ const string layout_var = MakeVar("layout");
+ std::string parent = "null";
+ if (!view_stack_.empty()) {
+ const StackEntry& parent_entry = view_stack_.back();
+ parent = parent_entry.view_var;
+ }
+ out_ << " xml.next(); // <" << class_name << ">\n"
+ << StringPrintf(" %s %s = createView(context, attrs, %s, \"%s\", factory, factory2);\n",
+ class_name.c_str(),
+ view_var.c_str(),
+ parent.c_str(),
+ class_name.c_str())
+ << StringPrintf(" if (%s == null) %s = new %s(context, attrs);\n",
+ view_var.c_str(),
+ view_var.c_str(),
+ class_name.c_str());
+ if (!view_stack_.empty()) {
+ out_ << StringPrintf(" ViewGroup.LayoutParams %s = %s.generateLayoutParams(attrs);\n",
+ layout_var.c_str(),
+ parent.c_str());
+ }
+ view_stack_.push_back({class_name, view_var, layout_var});
+}
+
+void JavaLangViewBuilder::FinishView() {
+ const StackEntry var = view_stack_.back();
+ view_stack_.pop_back();
+ if (!view_stack_.empty()) {
+ const string& parent = view_stack_.back().view_var;
+ out_ << StringPrintf(" xml.next(); // </%s>\n", var.class_name.c_str())
+ << StringPrintf(" %s.addView(%s, %s);\n",
+ parent.c_str(),
+ var.view_var.c_str(),
+ var.layout_params_var.c_str());
+ } else {
+ out_ << StringPrintf(" return %s;\n", var.view_var.c_str());
+ }
+}
+
+const std::string JavaLangViewBuilder::MakeVar(std::string prefix) {
+ std::stringstream v;
+ v << prefix << view_id_++;
+ return v.str();
+}
diff --git a/startop/tools/view_compiler/java_lang_builder.h b/startop/tools/view_compiler/java_lang_builder.h
new file mode 100644
index 0000000..c8d20b2
--- /dev/null
+++ b/startop/tools/view_compiler/java_lang_builder.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 JAVA_LANG_BUILDER_H_
+#define JAVA_LANG_BUILDER_H_
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+// Build Java language code to instantiate views.
+//
+// This has a very small interface to make it easier to generate additional
+// backends, such as a direct-to-DEX version.
+class JavaLangViewBuilder {
+ public:
+ JavaLangViewBuilder(std::string package, std::string layout_name, std::ostream& out = std::cout)
+ : package_(package), layout_name_(layout_name), out_(out) {}
+
+ // Begin generating a class. Adds the package boilerplate, etc.
+ void Start() const;
+ // Finish generating a class, closing off any open curly braces, etc.
+ void Finish() const;
+
+ // Begin creating a view (i.e. process the opening tag)
+ void StartView(const std::string& class_name);
+ // Finish a view, after all of its child nodes have been processed.
+ void FinishView();
+
+ private:
+ const std::string MakeVar(std::string prefix);
+
+ std::string const package_;
+ std::string const layout_name_;
+
+ std::ostream& out_;
+
+ size_t view_id_ = 0;
+
+ struct StackEntry {
+ // The class name for this view object
+ const std::string class_name;
+
+ // The variable name that is holding the view object
+ const std::string view_var;
+
+ // The variable name that holds the object's layout parameters
+ const std::string layout_params_var;
+ };
+ std::vector<StackEntry> view_stack_;
+};
+
+#endif // JAVA_LANG_BUILDER_H_
diff --git a/startop/tools/view_compiler/main.cc b/startop/tools/view_compiler/main.cc
new file mode 100644
index 0000000..0ad7e24
--- /dev/null
+++ b/startop/tools/view_compiler/main.cc
@@ -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.
+ */
+
+#include "gflags/gflags.h"
+
+#include "java_lang_builder.h"
+#include "util.h"
+
+#include "tinyxml2.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace tinyxml2;
+using std::string;
+
+constexpr char kStdoutFilename[]{"stdout"};
+
+DEFINE_string(package, "", "The package name for the generated class (required)");
+DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
+
+namespace {
+class ViewCompilerXmlVisitor : public XMLVisitor {
+ public:
+ ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+
+ bool VisitEnter(const XMLDocument& /*doc*/) override {
+ builder_->Start();
+ return true;
+ }
+
+ bool VisitExit(const XMLDocument& /*doc*/) override {
+ builder_->Finish();
+ return true;
+ }
+
+ bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
+ builder_->StartView(element.Name());
+ return true;
+ }
+
+ bool VisitExit(const XMLElement& /*element*/) override {
+ builder_->FinishView();
+ return true;
+ }
+
+ private:
+ JavaLangViewBuilder* builder_;
+};
+} // end namespace
+
+int main(int argc, char** argv) {
+ constexpr size_t kProgramName = 0;
+ constexpr size_t kFileNameParam = 1;
+ constexpr size_t kNumRequiredArgs = 2;
+
+ gflags::SetUsageMessage(
+ "Compile XML layout files into equivalent Java language code\n"
+ "\n"
+ " example usage: viewcompiler layout.xml --package com.example.androidapp");
+ gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
+
+ gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
+ if (argc != kNumRequiredArgs || cmd.is_default) {
+ gflags::ShowUsageWithFlags(argv[kProgramName]);
+ return 1;
+ }
+
+ const char* const filename = argv[kFileNameParam];
+ const string layout_name = FindLayoutNameFromFilename(filename);
+
+ // We want to generate Java language code to inflate exactly this layout. This means
+ // generating code to walk the resource XML too.
+
+ XMLDocument xml;
+ xml.LoadFile(filename);
+
+ std::ofstream outfile;
+ if (FLAGS_out != kStdoutFilename) {
+ outfile.open(FLAGS_out);
+ }
+ JavaLangViewBuilder builder{
+ FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile};
+
+ ViewCompilerXmlVisitor visitor{&builder};
+ xml.Accept(&visitor);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/startop/tools/view_compiler/util.cc b/startop/tools/view_compiler/util.cc
new file mode 100644
index 0000000..69df41d
--- /dev/null
+++ b/startop/tools/view_compiler/util.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+using std::string;
+
+// TODO: see if we can borrow this from somewhere else, like aapt2.
+string FindLayoutNameFromFilename(const string& filename) {
+ size_t start = filename.rfind("/");
+ if (start == string::npos) {
+ start = 0;
+ } else {
+ start++; // advance past '/' character
+ }
+ size_t end = filename.find(".", start);
+
+ return filename.substr(start, end - start);
+}
diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/startop/tools/view_compiler/util.h
similarity index 63%
rename from packages/SystemUI/res/values-h600dp/config.xml
rename to startop/tools/view_compiler/util.h
index 8616e3e..03e0939 100644
--- a/packages/SystemUI/res/values-h600dp/config.xml
+++ b/startop/tools/view_compiler/util.h
@@ -1,22 +1,23 @@
-<?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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 UTIL_H_
+#define UTIL_H_
-<resources>
- <!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">3</integer>
-</resources>
+#include <string>
+
+std::string FindLayoutNameFromFilename(const std::string& filename);
+
+#endif // UTIL_H_
diff --git a/startop/tools/view_compiler/util_test.cc b/startop/tools/view_compiler/util_test.cc
new file mode 100644
index 0000000..d1540d3
--- /dev/null
+++ b/startop/tools/view_compiler/util_test.cc
@@ -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.
+ */
+
+#include "util.h"
+
+#include "gtest/gtest.h"
+
+using std::string;
+
+TEST(UtilTest, FindLayoutNameFromFilename) {
+ EXPECT_EQ("bar", ::FindLayoutNameFromFilename("foo/bar.xml"));
+ EXPECT_EQ("bar", ::FindLayoutNameFromFilename("bar.xml"));
+ EXPECT_EQ("bar", ::FindLayoutNameFromFilename("./foo/bar.xml"));
+ EXPECT_EQ("bar", ::FindLayoutNameFromFilename("/foo/bar.xml"));
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 096cf37d..08bc9bc 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -141,6 +141,8 @@
* The caller must specify the {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE} to indicate to
* Telecom which {@link PhoneAccountHandle} the {@link Call} should be handed over to.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_REQUEST_HANDOVER =
"android.telecom.event.REQUEST_HANDOVER";
@@ -149,6 +151,8 @@
* Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event. Specifies the
* {@link PhoneAccountHandle} to which a call should be handed over to.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE =
"android.telecom.extra.HANDOVER_PHONE_ACCOUNT_HANDLE";
@@ -161,6 +165,8 @@
* {@link VideoProfile#STATE_BIDIRECTIONAL}, {@link VideoProfile#STATE_RX_ENABLED}, and
* {@link VideoProfile#STATE_TX_ENABLED}.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EXTRA_HANDOVER_VIDEO_STATE =
"android.telecom.extra.HANDOVER_VIDEO_STATE";
@@ -176,6 +182,8 @@
* {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
* is called to initate the handover.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EXTRA_HANDOVER_EXTRAS = "android.telecom.extra.HANDOVER_EXTRAS";
@@ -186,6 +194,8 @@
* <p>
* A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_COMPLETE =
"android.telecom.event.HANDOVER_COMPLETE";
@@ -198,6 +208,8 @@
* <p>
* A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_SOURCE_DISCONNECTED =
"android.telecom.event.HANDOVER_SOURCE_DISCONNECTED";
@@ -209,6 +221,8 @@
* <p>
* A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
@@ -434,8 +448,15 @@
*/
public static final int PROPERTY_RTT = 0x00000400;
+ /**
+ * Indicates that the call has been identified as the network as an emergency call. This
+ * property may be set for both incoming and outgoing calls which the network identifies as
+ * emergency calls.
+ */
+ public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 0x00000800;
+
//******************************************************************************************
- // Next PROPERTY value: 0x00000800
+ // Next PROPERTY value: 0x00001000
//******************************************************************************************
private final String mTelecomCallId;
@@ -601,6 +622,9 @@
if(hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
builder.append(" PROPERTY_ASSISTED_DIALING_USED");
}
+ if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
+ builder.append(" PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL");
+ }
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 024bd30..a39e885 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -22,6 +22,8 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.telecom.Connection.VideoProvider;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import java.util.ArrayList;
@@ -573,6 +575,20 @@
}
/**
+ * Updates RIL voice radio technology used for current conference after its creation.
+ *
+ * @hide
+ */
+ public void updateCallRadioTechAfterCreation() {
+ final Connection primaryConnection = getPrimaryConnection();
+ if (primaryConnection != null) {
+ setCallRadioTech(primaryConnection.getCallRadioTech());
+ } else {
+ Log.w(this, "No primary connection found while updateCallRadioTechAfterCreation");
+ }
+ }
+
+ /**
* @hide
* @deprecated Use {@link #setConnectionTime}.
*/
@@ -652,6 +668,37 @@
}
/**
+ * Sets RIL voice radio technology used for current conference.
+ *
+ * @param vrat the RIL voice radio technology used for current conference,
+ * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+ *
+ * @hide
+ */
+ public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
+ putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToNetworkType(vrat));
+ }
+
+ /**
+ * Returns RIL voice radio technology used for current conference.
+ *
+ * @return the RIL voice radio technology used for current conference,
+ * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+ *
+ * @hide
+ */
+ public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
+ int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ Bundle extras = getExtras();
+ if (extras != null) {
+ voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+ return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
+ }
+
+ /**
* Inform this Conference that the state of its audio output has been changed externally.
*
* @param state The new audio state.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3d2b397..34603a3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -38,6 +38,8 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.view.Surface;
@@ -412,6 +414,13 @@
*/
public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
+ /**
+ * Set by the framework to indicate that the network has identified a Connection as an emergency
+ * call.
+ * @hide
+ */
+ public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1 << 10;
+
//**********************************************************************************************
// Next PROPERTY value: 1<<10
//**********************************************************************************************
@@ -585,6 +594,8 @@
* {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has
* successfully completed.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_COMPLETE =
"android.telecom.event.HANDOVER_COMPLETE";
@@ -594,6 +605,8 @@
* {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has failed
* to complete.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
@@ -801,6 +814,10 @@
builder.append(isLong ? " PROPERTY_IS_RTT" : " rtt");
}
+ if (can(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
+ builder.append(isLong ? " PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL" : " ecall");
+ }
+
builder.append("]");
return builder.toString();
}
@@ -1885,6 +1902,24 @@
}
/**
+ * Returns RIL voice radio technology used for current connection.
+ *
+ * @return the RIL voice radio technology used for current connection,
+ * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+ *
+ * @hide
+ */
+ public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
+ int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ Bundle extras = getExtras();
+ if (extras != null) {
+ voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+ return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
+ }
+
+ /**
* @return The status hints for this connection.
*/
public final StatusHints getStatusHints() {
@@ -2318,6 +2353,26 @@
}
/**
+ * Sets RIL voice radio technology used for current connection.
+ *
+ * @param vrat the RIL Voice Radio Technology used for current connection,
+ * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+ *
+ * @hide
+ */
+ public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
+ putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToNetworkType(vrat));
+ // Propagates the call radio technology to its parent {@link android.telecom.Conference}
+ // This action only covers non-IMS CS conference calls.
+ // For IMS PS call conference call, it can be updated via its host connection
+ // {@link #Listener.onExtrasChanged} event.
+ if (getConference() != null) {
+ getConference().setCallRadioTech(vrat);
+ }
+ }
+
+ /**
* Sets the label and icon status to display in the in-call UI.
*
* @param statusHints The status label and icon to set.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4e22823..8c37a21 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -317,6 +317,15 @@
"android.telecom.extra.CALL_TECHNOLOGY_TYPE";
/**
+ * Optional extra for communicating the call network technology used by a
+ * {@link android.telecom.Connection} to Telecom and InCallUI.
+ *
+ * @see {@code NETWORK_TYPE_*} in {@link android.telephony.TelephonyManager}.
+ */
+ public static final String EXTRA_CALL_NETWORK_TYPE =
+ "android.telecom.extra.CALL_NETWORK_TYPE";
+
+ /**
* An optional {@link android.content.Intent#ACTION_CALL} intent extra denoting the
* package name of the app specifying an alternative gateway for the call.
* The value is a string.
@@ -1875,6 +1884,27 @@
}
}
+ /**
+ * Determines if there is an ongoing emergency call. This can be either an outgoing emergency
+ * call, as identified by the dialed number, or because a call was identified by the network
+ * as an emergency call.
+ * @return {@code true} if there is an ongoing emergency call, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean isInEmergencyCall() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().isInEmergencyCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException isInEmergencyCall: " + e);
+ return false;
+ }
+ return false;
+ }
+
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index b4e7d56..38247bc 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -279,4 +279,9 @@
* @see TelecomServiceImpl#acceptHandover
*/
void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct);
+
+ /**
+ * @see TelecomServiceImpl#isInEmergencyCall
+ */
+ boolean isInEmergencyCall();
}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 722df67..4846092 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -1233,6 +1233,17 @@
"android.provider.action.EXTERNAL_PROVIDER_CHANGE";
/**
+ * Same as {@link #ACTION_DEFAULT_SMS_PACKAGE_CHANGED} but it's implicit (e.g. sent to
+ * all apps) and requires
+ * {@link android.Manifest.permission#MONITOR_DEFAULT_SMS_PACKAGE} to receive.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL =
+ "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL";
+
+ /**
* Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
* {@link #DATA_SMS_RECEIVED_ACTION} intent.
*
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7506d00..57b652e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -271,6 +271,14 @@
KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
/**
+ * Do only allow auto selection in Advanced Network Settings when in home network.
+ * Manual selection is allowed when in roaming network.
+ * @hide
+ */
+ public static final String
+ KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
+
+ /**
* Control whether users receive a simplified network settings UI and improved network
* selection.
*/
@@ -1966,6 +1974,17 @@
"skip_cf_fail_to_disable_dialog_bool";
/**
+ * Flag specifying whether operator supports including no reply condition timer option on
+ * CFNRy (3GPP TS 24.082 3: Call Forwarding on No Reply) in the call forwarding settings UI.
+ * {@code true} - include no reply condition timer option on CFNRy
+ * {@code false} - don't include no reply condition timer option on CFNRy
+ *
+ * @hide
+ */
+ public static final String KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL =
+ "support_no_reply_timer_for_cfnry_bool";
+
+ /**
* List of the FAC (feature access codes) to dial as a normal call.
* @hide
*/
@@ -2104,6 +2123,16 @@
public static final String KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL =
"config_show_orig_dial_string_for_cdma";
+ /**
+ * Flag specifying whether to show notification(call blocking disabled) when Enhanced Call
+ * Blocking(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL) is enabled and making emergency call.
+ * When true, notification is shown always.
+ * When false, notification is shown only when any setting of "Enhanced Blocked number" is
+ * enabled.
+ */
+ public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL =
+ "show_call_blocking_disabled_notification_always_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2160,6 +2189,7 @@
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
@@ -2418,6 +2448,7 @@
sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
@@ -2441,6 +2472,7 @@
});
sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
new file mode 100644
index 0000000..e373797
--- /dev/null
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCall;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.InternalGroupCallCallback;
+import android.telephony.mbms.InternalGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class provides functionality for accessing group call functionality over MBMS.
+ */
+public class MbmsGroupCallSession implements AutoCloseable {
+ private static final String LOG_TAG = "MbmsGroupCallSession";
+
+ /**
+ * Service action which must be handled by the middleware implementing the MBMS group call
+ * interface.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String MBMS_GROUP_CALL_SERVICE_ACTION =
+ "android.telephony.action.EmbmsGroupCall";
+
+ /**
+ * Metadata key that specifies the component name of the service to bind to for group calls.
+ * @hide
+ */
+ @TestApi
+ public static final String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA =
+ "mbms-group-call-service-override";
+
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+ private AtomicReference<IMbmsGroupCallService> mService = new AtomicReference<>(null);
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sIsInitialized.set(false);
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Received death notification");
+ }
+ };
+
+ private InternalGroupCallSessionCallback mInternalCallback;
+ private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();
+
+ private final Context mContext;
+ private int mSubscriptionId;
+
+ /** @hide */
+ private MbmsGroupCallSession(Context context, Executor executor, int subscriptionId,
+ MbmsGroupCallSessionCallback callback) {
+ mContext = context;
+ mSubscriptionId = subscriptionId;
+ mInternalCallback = new InternalGroupCallSessionCallback(callback, executor);
+ }
+
+ /**
+ * Create a new {@link MbmsGroupCallSession} using the given subscription ID.
+ *
+ * You may only have one instance of {@link MbmsGroupCallSession} per UID. If you call this
+ * method while there is an active instance of {@link MbmsGroupCallSession} in your process
+ * (in other words, one that has not had {@link #close()} called on it), this method will
+ * throw an {@link IllegalStateException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsGroupCallSessionCallback#onError(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call {@link #close()} on the instance of
+ * {@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 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,
+ final @NonNull MbmsGroupCallSessionCallback callback) {
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
+ }
+ MbmsGroupCallSession session = new MbmsGroupCallSession(context, executor,
+ subscriptionId, callback);
+
+ final int result = session.bindAndInitialize();
+ if (result != MbmsErrors.SUCCESS) {
+ sIsInitialized.set(false);
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(result, null);
+ }
+ });
+ return null;
+ }
+ return session;
+ }
+
+ /**
+ * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
+ * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ */
+ public static MbmsGroupCallSession create(@NonNull Context context,
+ @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
+ return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+ }
+
+ /**
+ * Terminates this instance. Also terminates
+ * any group calls spawned from this instance as if
+ * {@link GroupCall#close()} had been called on them. After this method returns,
+ * no further callbacks originating from the middleware will be enqueued on the provided
+ * 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
+ * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
+ * returns.
+ *
+ * May throw an {@link IllegalStateException}
+ */
+ public void close() {
+ try {
+ IMbmsGroupCallService groupCallService = mService.get();
+ if (groupCallService == null) {
+ // Ignore and return, assume already disposed.
+ return;
+ }
+ groupCallService.dispose(mSubscriptionId);
+ for (GroupCall s : mKnownActiveGroupCalls) {
+ s.getCallback().stop();
+ }
+ mKnownActiveGroupCalls.clear();
+ } catch (RemoteException e) {
+ // Ignore for now
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
+ mInternalCallback.stop();
+ }
+ }
+
+ /**
+ * Starts the requested group call, reporting status to the indicated callback.
+ * Returns an object used to control that call.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * 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
+ * the carrier.
+ * @param frequencyArray An array of frequencies for the group call that should be negotiated
+ * separately with the carrier.
+ * @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) {
+ IMbmsGroupCallService groupCallService = mService.get();
+ if (groupCallService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ InternalGroupCallCallback serviceCallback = new InternalGroupCallCallback(
+ callback, executor);
+
+ GroupCall serviceForApp = new GroupCall(mSubscriptionId,
+ groupCallService, this, tmgi, serviceCallback);
+ mKnownActiveGroupCalls.add(serviceForApp);
+
+ try {
+ int returnCode = groupCallService.startGroupCall(
+ mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+ if (returnCode == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
+ if (returnCode != MbmsErrors.SUCCESS) {
+ mInternalCallback.onError(returnCode, null);
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ sIsInitialized.set(false);
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return null;
+ }
+
+ return serviceForApp;
+ }
+
+ /** @hide */
+ public void onGroupCallStopped(GroupCall service) {
+ mKnownActiveGroupCalls.remove(service);
+ }
+
+ private int bindAndInitialize() {
+ return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsGroupCallService groupCallService =
+ IMbmsGroupCallService.Stub.asInterface(service);
+ int result;
+ try {
+ result = groupCallService.initialize(mInternalCallback,
+ mSubscriptionId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ mInternalCallback.onError(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ mInternalCallback.onError(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return"
+ + " an unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ mInternalCallback.onError(result,
+ "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(groupCallService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
+ });
+ }
+}
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 8e99518..5e4518f 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -32,7 +32,11 @@
/**
* Represents the neighboring cell information, including
* Received Signal Strength and Cell ID location.
+ *
+ * @deprecated This class should not be used by anyone targeting SDK level 29 (Q) or higher.
+ * Instead callers should use {@Link android.telephony.CellInfo}.
*/
+@Deprecated
public class NeighboringCellInfo implements Parcelable
{
/**
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 7c7d7a0..202da68 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -16,11 +16,10 @@
package android.telephony;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.annotation.IntDef;
-import android.util.Log;
import com.android.internal.telephony.ITelephony;
@@ -113,6 +112,8 @@
}
try {
telephony.stopNetworkScan(mSubId, mScanId);
+ } catch (IllegalArgumentException ex) {
+ Rlog.d(TAG, "stopNetworkScan - no active scan for ScanID=" + mScanId);
} catch (RemoteException ex) {
Rlog.e(TAG, "stopNetworkScan RemoteException", ex);
} catch (RuntimeException ex) {
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 498be96..3ea018af 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -281,6 +281,16 @@
*/
public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000;
+ /**
+ * Listen for changes to preferred data subId.
+ * See {@link SubscriptionManager#setPreferredData(int)}
+ * for more details.
+ *
+ * @see #onPreferredDataSubIdChanged
+ * @hide
+ */
+ public static final int LISTEN_PREFERRED_DATA_SUBID_CHANGE = 0x00400000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -407,6 +417,9 @@
PhoneStateListener.this.onPhoneCapabilityChanged(
(PhoneCapability) msg.obj);
break;
+ case LISTEN_PREFERRED_DATA_SUBID_CHANGE:
+ PhoneStateListener.this.onPreferredDataSubIdChanged((int) msg.obj);
+ break;
}
}
};
@@ -647,6 +660,18 @@
}
/**
+ * Callback invoked when preferred data subId changes. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param subId the new preferred data subId. If it's INVALID_SUBSCRIPTION_ID,
+ * it means it's unset and defaultDataSub is used to determine which
+ * modem is preferred.
+ * @hide
+ */
+ public void onPreferredDataSubIdChanged(int subId) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -777,6 +802,11 @@
public void onPhoneCapabilityChanged(PhoneCapability capability) {
send(LISTEN_PHONE_CAPABILITY_CHANGE, 0, 0, capability);
}
+
+ public void onPreferredDataSubIdChanged(int subId) {
+ send(LISTEN_PREFERRED_DATA_SUBID_CHANGE, 0, 0, subId);
+ }
+
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 91e24a9..e40bae1 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -603,7 +603,7 @@
/**
* Returns the message body as a String, if it exists and is text based.
- * @return message body is there is one, otherwise null
+ * @return message body if there is one, otherwise null
*/
public String getMessageBody() {
return mWrappedSmsMessage.getMessageBody();
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index ec26622..22c1e58 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -33,11 +33,13 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* A Parcelable class for Subscription Information.
@@ -552,11 +554,49 @@
String cardIdToPrint = givePrintableIccid(mCardId);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
- + " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " mNumber=" + mNumber
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
- + " mnc " + mMnc + " isEmbedded " + mIsEmbedded
+ + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ " accessRules " + Arrays.toString(mAccessRules)
+ " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
+ " parentSubId=" + mParentSubId + "}";
}
-}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
+ mIsOpportunistic, mParentSubId, mIccId, mNumber, mMcc, mMnc, mCountryIso,
+ mCardId, mDisplayName, mCarrierName, mAccessRules);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (obj == this) return true;
+
+ SubscriptionInfo toCompare;
+ try {
+ toCompare = (SubscriptionInfo) obj;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ return mId == toCompare.mId
+ && mSimSlotIndex == toCompare.mSimSlotIndex
+ && mNameSource == toCompare.mNameSource
+ && mIconTint == toCompare.mIconTint
+ && mDataRoaming == toCompare.mDataRoaming
+ && mIsEmbedded == toCompare.mIsEmbedded
+ && mIsOpportunistic == toCompare.mIsOpportunistic
+ && mParentSubId == toCompare.mParentSubId
+ && Objects.equals(mIccId, toCompare.mIccId)
+ && Objects.equals(mNumber, toCompare.mNumber)
+ && Objects.equals(mMcc, toCompare.mMcc)
+ && Objects.equals(mMnc, toCompare.mMnc)
+ && Objects.equals(mCountryIso, toCompare.mCountryIso)
+ && Objects.equals(mCardId, toCompare.mCardId)
+ && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
+ && 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 e9423f7..cc143d6 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -86,9 +86,8 @@
/** @hide */
public static final int INVALID_PHONE_INDEX = -1;
- /** An invalid slot identifier */
- /** @hide */
- public static final int INVALID_SIM_SLOT_INDEX = -1;
+ /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */
+ public static final int INVALID_SIM_SLOT_INDEX = -2;
/** Indicates the caller wants the default sub id. */
/** @hide */
@@ -139,9 +138,8 @@
/** @hide */
public static final String SIM_SLOT_INDEX = "sim_id";
- /** SIM is not inserted */
- /** @hide */
- public static final int SIM_NOT_INSERTED = -1;
+ /** Indicates SIM is not inserted. This can be returned by {@link #getSlotIndex(int)}. */
+ public static final int SIM_NOT_INSERTED = -3;
/**
* TelephonyProvider column name for user displayed name.
@@ -1264,16 +1262,22 @@
/**
* Get slotIndex associated with the subscription.
- * @return slotIndex as a positive integer or a negative value if an error either
- * SIM_NOT_INSERTED or < 0 if an invalid slot index
- * @hide
+ *
+ * @param subscriptionId the unique SubscriptionInfo index in database
+ * @return slotIndex as a positive integer or a negative value,
+ * <ol>
+ * <li>{@link #INVALID_SUBSCRIPTION_ID} if the supplied subscriptionId is invalid </li>
+ * <li>{@link #SIM_NOT_INSERTED} if sim is not inserted </li>
+ * <li>{@link #INVALID_SIM_SLOT_INDEX} if the supplied subscriptionId doesn't have an
+ * associated slot index </li>
+ * </ol>
*/
- @UnsupportedAppUsage
- public static int getSlotIndex(int subId) {
- if (!isValidSubscriptionId(subId)) {
+ public static int getSlotIndex(int subscriptionId) {
+ if (!isValidSubscriptionId(subscriptionId)) {
if (DBG) {
- logd("[getSlotIndex]- fail");
+ logd("[getSlotIndex]- supplied subscriptionId is invalid. ");
}
+ return INVALID_SUBSCRIPTION_ID;
}
int result = INVALID_SIM_SLOT_INDEX;
@@ -1281,7 +1285,7 @@
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getSlotIndex(subId);
+ result = iSub.getSlotIndex(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
@@ -1848,6 +1852,19 @@
}
/**
+ * Checks if the supplied subscription ID corresponds to an active subscription.
+ *
+ * @param subscriptionId the subscription ID.
+ * @return {@code true} if the supplied subscription ID corresponds to an active subscription;
+ * {@code false} if it does not correspond to an active subscription; or throw a
+ * SecurityException if the caller hasn't got the right permission.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public boolean isActiveSubscriptionId(int subscriptionId) {
+ return isActiveSubId(subscriptionId);
+ }
+
+ /**
* @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
* and the SIM providing the subscription is present in a slot and in "LOADED" state.
* @hide
@@ -1857,7 +1874,7 @@
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- return iSub.isActiveSubId(subId);
+ return iSub.isActiveSubId(subId, mContext.getOpPackageName());
}
} catch (RemoteException ex) {
}
@@ -2136,9 +2153,15 @@
/**
* Set preferred default data.
- * Set on which slot default data will be on.
+ * Set on which slot most cellular data will be on.
+ * It's also usually what we set up internet connection on.
*
- * @param slotId which slot is preferred to for cellular data.
+ * PreferredData overwrites user setting of default data subscription. And it's used
+ * by AlternativeNetworkAccessService or carrier apps to switch primary and CBRS
+ * subscription dynamically in multi-SIM devices.
+ *
+ * @param slotId which slot is preferred to for cellular data. If it's INVALID, it means
+ * it's unset and defaultDataSubId is used to determine which modem is preferred.
* @hide
*
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b5d1f06..ea9ac39 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.content.Context.TELECOM_SERVICE;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
@@ -69,6 +71,7 @@
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyProperties;
@@ -1200,6 +1203,15 @@
"android.intent.action.DATA_STALL_DETECTED";
/**
+ * A service action that identifies a {@link android.app.SmsAppService} subclass in the
+ * AndroidManifest.xml.
+ *
+ * <p>See {@link android.app.SmsAppService} for the details.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE";
+
+ /**
* An int extra used with {@link #ACTION_DATA_STALL_DETECTED} to indicate the
* action associated with the data stall recovery.
*
@@ -1578,6 +1590,7 @@
*
* @return List of NeighboringCellInfo or null if info unavailable.
*
+ * @removed
* @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
* from NeighboringCellInfo, including LTE cell information.
*/
@@ -2090,10 +2103,37 @@
/** 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;
+
+ /** @hide */
+ @IntDef({
+ NETWORK_TYPE_UNKNOWN,
+ NETWORK_TYPE_GPRS,
+ NETWORK_TYPE_EDGE,
+ NETWORK_TYPE_UMTS,
+ NETWORK_TYPE_CDMA,
+ NETWORK_TYPE_EVDO_0,
+ NETWORK_TYPE_EVDO_A,
+ NETWORK_TYPE_1xRTT,
+ NETWORK_TYPE_HSDPA,
+ NETWORK_TYPE_HSUPA,
+ NETWORK_TYPE_HSPA,
+ NETWORK_TYPE_IDEN,
+ NETWORK_TYPE_EVDO_B,
+ NETWORK_TYPE_LTE,
+ NETWORK_TYPE_EHRPD,
+ NETWORK_TYPE_HSPAP,
+ NETWORK_TYPE_GSM,
+ NETWORK_TYPE_TD_SCDMA,
+ NETWORK_TYPE_IWLAN,
+ NETWORK_TYPE_LTE_CA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkType{}
+
/**
* @return the NETWORK_TYPE_xxxx for current data connection.
*/
- public int getNetworkType() {
+ public @NetworkType int getNetworkType() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -2138,24 +2178,24 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage
- public int getNetworkType(int subId) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
- } else {
- // This can happen when the ITelephony interface is not up yet.
- return NETWORK_TYPE_UNKNOWN;
- }
- } catch(RemoteException ex) {
- // This shouldn't happen in the normal case
- return NETWORK_TYPE_UNKNOWN;
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- return NETWORK_TYPE_UNKNOWN;
- }
- }
+ @UnsupportedAppUsage
+ public int getNetworkType(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ } catch (RemoteException ex) {
+ // This shouldn't happen in the normal case
+ return NETWORK_TYPE_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ }
/**
* Returns a constant indicating the radio technology (network type)
@@ -2188,7 +2228,7 @@
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getDataNetworkType() {
+ public @NetworkType int getDataNetworkType() {
return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
@@ -2228,7 +2268,7 @@
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getVoiceNetworkType() {
+ public @NetworkType int getVoiceNetworkType() {
return getVoiceNetworkType(getSubId());
}
@@ -4107,11 +4147,16 @@
}
/**
- * Returns the IMS home network domain name that was loaded from the ISIM.
- * @return the IMS domain name, or null if not present or not loaded
+ * Returns the IMS home network domain name that was loaded from the ISIM {@see #APPTYPE_ISIM}.
+ * @return the IMS domain name. Returns {@code null} if ISIM hasn't been loaded or IMS domain
+ * hasn't been loaded or isn't present on the ISIM.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimDomain() {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -4339,7 +4384,7 @@
* @hide
*/
private ITelecomService getTelecomService() {
- return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
+ return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
}
private ITelephonyRegistry getTelephonyRegistry() {
@@ -5369,7 +5414,19 @@
}
}
- // ICC SIM Application Types
+ /**
+ * UICC SIM Application Types
+ * @hide
+ */
+ @IntDef(prefix = { "APPTYPE_" }, value = {
+ APPTYPE_SIM,
+ APPTYPE_USIM,
+ APPTYPE_RUIM,
+ APPTYPE_CSIM,
+ APPTYPE_ISIM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UiccAppType{}
/** UICC application type is SIM */
public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
/** UICC application type is USIM */
@@ -5380,6 +5437,7 @@
public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM;
/** UICC application type is ISIM */
public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM;
+
// authContext (parameter P2) when doing UICC challenge,
// per 3GPP TS 31.102 (Section 7.1.2)
/** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */
@@ -5646,6 +5704,202 @@
}
}
+ /** @hide */
+ @IntDef(prefix = { "NETWORK_MODE_" }, value = {
+ NETWORK_MODE_WCDMA_PREF,
+ NETWORK_MODE_GSM_ONLY,
+ NETWORK_MODE_WCDMA_ONLY,
+ NETWORK_MODE_GSM_UMTS,
+ NETWORK_MODE_CDMA_EVDO,
+ NETWORK_MODE_CDMA_NO_EVDO,
+ NETWORK_MODE_EVDO_NO_CDMA,
+ NETWORK_MODE_GLOBAL,
+ NETWORK_MODE_LTE_CDMA_EVDO,
+ NETWORK_MODE_LTE_GSM_WCDMA,
+ NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+ NETWORK_MODE_LTE_ONLY,
+ NETWORK_MODE_LTE_WCDMA,
+ NETWORK_MODE_TDSCDMA_ONLY,
+ NETWORK_MODE_TDSCDMA_WCDMA,
+ NETWORK_MODE_LTE_TDSCDMA,
+ NETWORK_MODE_TDSCDMA_GSM,
+ NETWORK_MODE_LTE_TDSCDMA_GSM,
+ NETWORK_MODE_TDSCDMA_GSM_WCDMA,
+ NETWORK_MODE_LTE_TDSCDMA_WCDMA,
+ NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
+ NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+ NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PrefNetworkMode{}
+
+ /**
+ * preferred network mode is GSM/WCDMA (WCDMA preferred).
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
+
+ /**
+ * preferred network mode is GSM only.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
+
+ /**
+ * preferred network mode is WCDMA only.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+
+ /**
+ * preferred network mode is GSM/WCDMA (auto mode, according to PRL).
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
+
+ /**
+ * preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
+
+ /**
+ * preferred network mode is CDMA only.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+
+ /**
+ * preferred network mode is EvDo only.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+
+ /**
+ * preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
+
+ /**
+ * preferred network mode is LTE, CDMA and EvDo.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+
+ /**
+ * preferred network mode is LTE, GSM/WCDMA.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+
+ /**
+ * preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+
+ /**
+ * preferred network mode is LTE Only.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
+
+ /**
+ * preferred network mode is LTE/WCDMA.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
+
+ /**
+ * preferred network mode is TD-SCDMA only.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+
+ /**
+ * preferred network mode is TD-SCDMA and WCDMA.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+
+ /**
+ * preferred network mode is TD-SCDMA and LTE.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+
+ /**
+ * preferred network mode is TD-SCDMA and GSM.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+
+ /**
+ * preferred network mode is TD-SCDMA,GSM and LTE.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
+ RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+
+ /**
+ * preferred network mode is TD-SCDMA, GSM/WCDMA.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+
+ /**
+ * preferred network mode is TD-SCDMA, WCDMA and LTE.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
+ RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+
+ /**
+ * preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+
+ /**
+ * preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ /**
+ * preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
/**
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
@@ -5654,11 +5908,12 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
- * @return the preferred network type, defined in RILConstants.java.
+ * @return the preferred network type.
* @hide
*/
- @UnsupportedAppUsage
- public int getPreferredNetworkType(int subId) {
+ @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
+ @SystemApi
+ public @PrefNetworkMode int getPreferredNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
@@ -5789,21 +6044,46 @@
* @param persistSelection whether the selection will persist until reboot. If true, only allows
* attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
* normal network selection next time.
- * @return true on success; false on any failure.
+ * @return {@code true} on success; {@code false} on any failure.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
+ return setNetworkSelectionModeManual(
+ new OperatorInfo(
+ "" /* operatorAlphaLong */, "" /* operatorAlphaShort */, operatorNumeric),
+ persistSelection);
+ }
+
+ /**
+ * Ask the radio to connect to the input network and change selection mode to manual.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
+ * @return {@code true} on success; {@code true} on any failure.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setNetworkSelectionModeManual(
+ OperatorInfo operatorInfo, boolean persistSelection) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.setNetworkSelectionModeManual(
- getSubId(), operatorNumeric, persistSelection);
+ getSubId(), operatorInfo, persistSelection);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
- } catch (NullPointerException ex) {
- Rlog.e(TAG, "setNetworkSelectionModeManual NPE", ex);
}
return false;
}
@@ -6215,7 +6495,7 @@
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
+ * @removed Use {@link android.telecom.TelecomManager#endCall()} instead.
* @hide
* @removed
*/
@@ -6227,7 +6507,7 @@
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+ * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
* @hide
* @removed
*/
@@ -6235,26 +6515,22 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void answerRingingCall() {
-
+ // No-op
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#silenceRinger} instead
+ * @removed Use {@link android.telecom.TelecomManager#silenceRinger} instead
* @hide
*/
@Deprecated
@SystemApi
@SuppressLint("Doclava125")
public void silenceRinger() {
- try {
- getTelecomService().silenceRinger(getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
- }
+ // No-op
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+ * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
* @hide
*/
@Deprecated
@@ -6264,18 +6540,11 @@
android.Manifest.permission.READ_PHONE_STATE
})
public boolean isOffhook() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.isOffhook(getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#isOffhook", e);
- }
return false;
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
+ * @removed Use {@link android.telecom.TelecomManager#isRinging} instead
* @hide
*/
@Deprecated
@@ -6285,18 +6554,11 @@
android.Manifest.permission.READ_PHONE_STATE
})
public boolean isRinging() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.isRinging(getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#isRinging", e);
- }
return false;
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+ * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
* @hide
*/
@Deprecated
@@ -6306,13 +6568,6 @@
android.Manifest.permission.READ_PHONE_STATE
})
public boolean isIdle() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.isIdle(getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#isIdle", e);
- }
return true;
}
@@ -7798,26 +8053,23 @@
}
/**
- * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+ * 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
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
*
- * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
- *
- * @param appType the uicc app type like {@link APPTYPE_CSIM}
- * @return Application ID for specificied app type or null if no uicc or error.
+ * @param appType the uicc app type.
+ * @return Application ID for specified app type or {@code null} if no uicc or error.
* @hide
*/
- public String getAidForAppType(int appType) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public String getAidForAppType(@UiccAppType int appType) {
return getAidForAppType(getSubId(), appType);
}
/**
- * Return the application ID for the app type like {@link APPTYPE_CSIM}.
- *
- * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
- *
- * @param subId the subscription ID that this request applies to.
- * @param appType the uicc app type, like {@link APPTYPE_CSIM}
- * @return Application ID for specificied app type or null if no uicc or error.
+ * same as {@link #getAidForAppType(int)}
* @hide
*/
public String getAidForAppType(int subId, int appType) {
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index f0d3c89..5d6a8c1 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -118,7 +118,9 @@
*/
public static final String EXTRA_CONFERENCE = "conference";
/**
- * @hide
+ * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
+ * emergency call. The {@link ImsService} sets this on a call to indicate that the network has
+ * identified the call as an emergency call.
*/
public static final String EXTRA_E_CALL = "e_call";
/**
@@ -245,7 +247,8 @@
* constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
* "14" vs (int) 14).
* Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
- * updateWifiStateFromExtras(Bundle)} to determine whether to set the
+ * updateImsCallRatFromExtras(Bundle)} to determine whether to set the
+ * {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
* {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
*/
public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
new file mode 100644
index 0000000..9aca18e
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/GroupCall.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.mbms;
+
+import android.annotation.IntDef;
+import android.os.RemoteException;
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class used to represent a single MBMS group call. After a call has been started with
+ * {@link MbmsGroupCallSession#startGroupCall},
+ * this class is used to hold information about the call and control it.
+ */
+public class GroupCall implements AutoCloseable {
+ private static final String LOG_TAG = "MbmsGroupCall";
+
+ /**
+ * The state of a group call, reported via
+ * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+ public @interface GroupCallState {}
+ public static final int STATE_STOPPED = 1;
+ public static final int STATE_STARTED = 2;
+ public static final int STATE_STALLED = 3;
+
+ /**
+ * The reason for a call state change, reported via
+ * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "REASON_" },
+ value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT,
+ REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+ REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
+ public @interface GroupCallStateChangeReason {}
+
+ /**
+ * Indicates that the middleware does not have a reason to provide for the state change.
+ */
+ public static final int REASON_NONE = 0;
+
+ /**
+ * State changed due to a call to {@link #close()} or
+ * {@link MbmsGroupCallSession#startGroupCall}
+ */
+ public static final int REASON_BY_USER_REQUEST = 1;
+
+ // 2 is unused to match up with streaming.
+
+ /**
+ * State changed due to a frequency conflict with another requested call.
+ */
+ public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+ /**
+ * State changed due to the middleware running out of memory
+ */
+ public static final int REASON_OUT_OF_MEMORY = 4;
+
+ /**
+ * State changed due to the device leaving the home carrier's LTE network.
+ */
+ public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+ /**
+ * State changed due to the device leaving the area where this call is being broadcast.
+ */
+ public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
+
+ private final int mSubscriptionId;
+ private final long mTmgi;
+ private final MbmsGroupCallSession mParentSession;
+ private final InternalGroupCallCallback mCallback;
+ private IMbmsGroupCallService mService;
+
+ /**
+ * @hide
+ */
+ public GroupCall(int subscriptionId,
+ IMbmsGroupCallService service,
+ MbmsGroupCallSession session,
+ long tmgi,
+ InternalGroupCallCallback callback) {
+ mSubscriptionId = subscriptionId;
+ mParentSession = session;
+ mService = service;
+ mTmgi = tmgi;
+ mCallback = callback;
+ }
+
+ /**
+ * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call.
+ */
+ public long getTmgi() {
+ return mTmgi;
+ }
+
+ /**
+ * 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.
+ */
+ public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+ if (mService == null) {
+ throw new IllegalStateException("No group call service attached");
+ }
+
+ try {
+ mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService = null;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ } finally {
+ mParentSession.onGroupCallStopped(this);
+ }
+ }
+
+ /**
+ * Stop this group call. Further operations on this object will fail with an
+ * {@link IllegalStateException}.
+ *
+ * May throw an {@link IllegalStateException}
+ */
+ @Override
+ public void close() {
+ if (mService == null) {
+ throw new IllegalStateException("No group call service attached");
+ }
+
+ try {
+ mService.stopGroupCall(mSubscriptionId, mTmgi);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService = null;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ } finally {
+ mParentSession.onGroupCallStopped(this);
+ }
+ }
+
+ /** @hide */
+ public InternalGroupCallCallback getCallback() {
+ return mCallback;
+ }
+
+ private void sendErrorToApp(int errorCode, String message) {
+ mCallback.onError(errorCode, message);
+ }
+}
+
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
new file mode 100644
index 0000000..001bb02
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.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.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * 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 {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ 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{}
+
+ /**
+ * Indicates broadcast signal strength is not available for this call.
+ *
+ * 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;
+
+ /**
+ * Called by the middleware when it has detected an error condition in this group call. The
+ * possible error codes are listed in {@link MbmsErrors}.
+ * @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
+ }
+
+ /**
+ * Called to indicate this call has changed state.
+ *
+ * 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
+ }
+
+ /**
+ * Broadcast Signal Strength updated.
+ *
+ * This signal strength is the BROADCAST signal strength which,
+ * depending on technology in play and it's deployment, may be
+ * stronger or weaker than the traditional UNICAST signal
+ * strength. It a simple int from 0-4 for valid levels or
+ * {@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
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
old mode 100644
new mode 100755
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
rename to telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
index 5d19851..844b634
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
+++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -11,15 +11,16 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.stackdivider.events;
-
-import com.android.systemui.recents.events.EventBus;
+package android.telephony.mbms;
/**
- * Sent when the divider is being draged either manually or by an animation.
+ * @hide
*/
-public class StartedDragingEvent extends EventBus.Event {
+oneway interface IGroupCallCallback {
+ void onError(int errorCode, String message);
+ void onGroupCallStateChanged(int state, int reason);
+ void onBroadcastSignalStrengthUpdated(int signalStrength);
}
diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
new file mode 100755
index 0000000..1a1c7f8
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
@@ -0,0 +1,33 @@
+/*
+** 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.telephony.mbms;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+oneway interface IMbmsGroupCallSessionCallback
+{
+ void onError(int errorCode, String message);
+
+ void onAvailableSaisUpdated(in List currentSai, in List availableSais);
+
+ void onServiceInterfaceAvailable(String interfaceName, int index);
+
+ void onMiddlewareReady();
+}
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
new file mode 100644
index 0000000..2910bb3
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.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 android.telephony.mbms;
+
+import android.os.Binder;
+
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallCallback extends IGroupCallCallback.Stub {
+ private final GroupCallCallback mAppCallback;
+ private final Executor mExecutor;
+ private volatile boolean mIsStopped = false;
+
+ public InternalGroupCallCallback(GroupCallCallback appCallback,
+ Executor executor) {
+ mAppCallback = appCallback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onError(final int errorCode, final String message) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onGroupCallStateChanged(final int state, final int reason) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onGroupCallStateChanged(state, reason);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ /** Prevents this callback from calling the app */
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
new file mode 100644
index 0000000..4c9cf4d
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallback.Stub {
+ private final Executor mExecutor;
+ private final MbmsGroupCallSessionCallback mAppCallback;
+ private volatile boolean mIsStopped = false;
+
+ public InternalGroupCallSessionCallback(MbmsGroupCallSessionCallback appCallback,
+ Executor executor) {
+ mAppCallback = appCallback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onError(final int errorCode, final String message) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAvailableSaisUpdated(final List currentSais, final List availableSais) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onAvailableSaisUpdated(currentSais, availableSais);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onServiceInterfaceAvailable(final String interfaceName, final int index) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onServiceInterfaceAvailable(interfaceName, index);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onMiddlewareReady() {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onMiddlewareReady();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ /** Prevents this callback from calling the app */
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
new file mode 100644
index 0000000..7da734e
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.MbmsGroupCallSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * 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)}.
+ */
+public class MbmsGroupCallSessionCallback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+ MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ 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{}
+
+ /**
+ * Called by the middleware when it has detected an error condition. The possible error codes
+ * are listed in {@link MbmsErrors}.
+ * @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) {
+ }
+
+ /**
+ * Indicates that the list of currently available SAIs has been updated. The app may use this
+ * information to filter the list of group calls when displaying available group calls to
+ * the user by matching the SAIs with a list of group calls separately negotiated with the
+ * carrier. The app may also report the aggregate list of SAIs to the group call application
+ * server so that a network entity can determine when, and where to activate the broadcast of
+ * particular group calls.
+ * @param currentSais The available SAIs on the current cell.
+ * @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) {
+ }
+
+ /**
+ * 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()}.
+ *
+ * @param interfaceName The interface name for the data link.
+ * @param index The index for the data link.
+ */
+ public void onServiceInterfaceAvailable(String interfaceName, int index) {
+ }
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link MbmsGroupCallSession} will result in an {@link IllegalStateException} or an error
+ * delivered via {@link #onError(int, String)} with error code
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
+ */
+ public void onMiddlewareReady() {
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index 06b2120..95b4d37 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -23,6 +23,7 @@
import android.content.pm.*;
import android.content.pm.ServiceInfo;
import android.telephony.MbmsDownloadSession;
+import android.telephony.MbmsGroupCallSession;
import android.telephony.MbmsStreamingSession;
import android.util.Log;
@@ -59,6 +60,9 @@
case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION:
metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA;
break;
+ case MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_ACTION:
+ metaDataKey = MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA;
+ break;
}
if (metaDataKey == null) {
return null;
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
new file mode 100755
index 0000000..721256a
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -0,0 +1,39 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony.mbms.vendor;
+
+import android.net.Uri;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.IGroupCallCallback;
+
+/**
+ * @hide
+ */
+interface IMbmsGroupCallService
+{
+ int initialize(IMbmsGroupCallSessionCallback callback, int subId);
+
+ void stopGroupCall(int subId, long tmgi);
+
+ void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
+ in int[] frequencyArray);
+
+ int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
+ in int[] frequencyArray, 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
new file mode 100644
index 0000000..3734ca7
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.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.telephony.mbms.vendor;
+
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.IGroupCallCallback;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub;
+
+import java.util.List;
+
+/**
+ * Base class for MBMS group-call services. The middleware should override this class to implement
+ * its {@link Service} for group calls
+ * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they
+ * override {@link #onBind}.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class MbmsGroupCallServiceBase extends Service {
+ private final IBinder mInterface = new Stub() {
+ @Override
+ public int initialize(final IMbmsGroupCallSessionCallback callback,
+ final int subscriptionId) throws RemoteException {
+ if (callback == null) {
+ throw new NullPointerException("Callback must not be null");
+ }
+
+ final int uid = Binder.getCallingUid();
+
+ int result = MbmsGroupCallServiceBase.this.initialize(
+ new MbmsGroupCallSessionCallback() {
+ @Override
+ public void onError(final int errorCode, final String message) {
+ try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
+ callback.onError(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onAvailableSaisUpdated(final List currentSais,
+ final List availableSais) {
+ try {
+ callback.onAvailableSaisUpdated(currentSais, availableSais);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onServiceInterfaceAvailable(final String interfaceName,
+ final int index) {
+ try {
+ callback.onServiceInterfaceAvailable(interfaceName, index);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onMiddlewareReady() {
+ try {
+ callback.onMiddlewareReady();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ }, subscriptionId);
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
+ }
+
+ @Override
+ public void stopGroupCall(int subId, long tmgi) {
+ MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi);
+ }
+
+ @Override
+ public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
+ int[] frequencyArray) {
+ MbmsGroupCallServiceBase.this.updateGroupCall(
+ subscriptionId, tmgi, saiArray, frequencyArray);
+ }
+
+ @Override
+ public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
+ final int[] frequencyArray, final IGroupCallCallback callback)
+ throws RemoteException {
+ if (callback == null) {
+ throw new NullPointerException("Callback must not be null");
+ }
+
+ final int uid = Binder.getCallingUid();
+
+ int result = MbmsGroupCallServiceBase.this.startGroupCall(
+ subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+ @Override
+ public void onError(final int errorCode, final String message) {
+ try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
+ callback.onError(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ public void onGroupCallStateChanged(int state, int reason) {
+ try {
+ callback.onGroupCallStateChanged(state, reason);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ try {
+ callback.onBroadcastSignalStrengthUpdated(signalStrength);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ });
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
+ }
+
+ @Override
+ public void dispose(int subId) throws RemoteException {
+ MbmsGroupCallServiceBase.this.dispose(subId);
+ }
+ };
+
+ /**
+ * Initialize the group call service for this app and subscription ID, registering the callback.
+ *
+ * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
+ * will be intercepted and passed to the app as
+ * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+ *
+ * May return any value from {@link MbmsErrors.InitializationErrors}
+ * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+ * {@link IMbmsGroupCallSessionCallback#onError(int, String)}.
+ *
+ * @param callback The callback to use to communicate with the app.
+ * @param subscriptionId The subscription ID to use.
+ */
+ public int initialize(MbmsGroupCallSessionCallback callback, int subscriptionId)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Starts a particular group call. This method may perform asynchronous work. When
+ * the call is ready for consumption, the middleware should inform the app via
+ * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @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 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) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Stop the group call identified by {@code tmgi}.
+ *
+ * The callback provided via {@link #startGroupCall} should no longer be
+ * used after this method has called by the app.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param tmgi The TMGI for the call to stop.
+ */
+ public void stopGroupCall(int subscriptionId, long tmgi) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * 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.
+ */
+ public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
+ int[] frequencyArray) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Signals that the app wishes to dispose of the session identified by the
+ * {@code subscriptionId} argument and the caller's uid. No notification back to the
+ * app is required for this operation, and the corresponding callback provided via
+ * {@link #initialize} should no longer be used
+ * after this method has been called by the app.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ */
+ public void dispose(int subscriptionId) throws RemoteException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Indicates that the app identified by the given UID and subscription ID has died.
+ * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+ * @param subscriptionId The subscription ID the app is using.
+ */
+ public void onAppCallbackDied(int uid, int subscriptionId) {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mInterface;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 1ebb697..38a1bc7 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -52,5 +52,6 @@
void onCarrierNetworkChange(in boolean active);
void onUserMobileDataStateChanged(in boolean enabled);
void onPhoneCapabilityChanged(in PhoneCapability capability);
+ void onPreferredDataSubIdChanged(in int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6521f0b..0ccd748 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -232,5 +232,5 @@
*/
int getSimStateForSlotIndex(int slotIndex);
- boolean isActiveSubId(int subId);
+ boolean isActiveSubId(int subId, String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c0bccde..ca2bcff 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -75,116 +75,6 @@
void call(String callingPackage, String number);
/**
- * End call if there is a call in progress, otherwise does nothing.
- *
- * @return whether it hung up
- */
- boolean endCall();
-
- /**
- * End call on particular subId or go to the Home screen
- * @param subId user preferred subId.
- * @return whether it hung up
- */
- boolean endCallForSubscriber(int subId);
-
- /**
- * Answer the currently-ringing call.
- *
- * If there's already a current active call, that call will be
- * automatically put on hold. If both lines are currently in use, the
- * current active call will be ended.
- *
- * TODO: provide a flag to let the caller specify what policy to use
- * if both lines are in use. (The current behavior is hardwired to
- * "answer incoming, end ongoing", which is how the CALL button
- * is specced to behave.)
- *
- * TODO: this should be a oneway call (especially since it's called
- * directly from the key queue thread).
- */
- void answerRingingCall();
-
- /**
- * Answer the currently-ringing call on particular subId .
- *
- * If there's already a current active call, that call will be
- * automatically put on hold. If both lines are currently in use, the
- * current active call will be ended.
- *
- * TODO: provide a flag to let the caller specify what policy to use
- * if both lines are in use. (The current behavior is hardwired to
- * "answer incoming, end ongoing", which is how the CALL button
- * is specced to behave.)
- *
- * TODO: this should be a oneway call (especially since it's called
- * directly from the key queue thread).
- */
- void answerRingingCallForSubscriber(int subId);
-
- /**
- * Silence the ringer if an incoming call is currently ringing.
- * (If vibrating, stop the vibrator also.)
- *
- * It's safe to call this if the ringer has already been silenced, or
- * even if there's no incoming call. (If so, this method will do nothing.)
- *
- * TODO: this should be a oneway call too (see above).
- * (Actually *all* the methods here that return void can
- * probably be oneway.)
- */
- void silenceRinger();
-
- /**
- * Check if we are in either an active or holding call
- * @param callingPackage the name of the package making the call.
- * @return true if the phone state is OFFHOOK.
- */
- boolean isOffhook(String callingPackage);
-
- /**
- * Check if a particular subId has an active or holding call
- *
- * @param subId user preferred subId.
- * @param callingPackage the name of the package making the call.
- * @return true if the phone state is OFFHOOK.
- */
- boolean isOffhookForSubscriber(int subId, String callingPackage);
-
- /**
- * Check if an incoming phone call is ringing or call waiting
- * on a particular subId.
- *
- * @param subId user preferred subId.
- * @param callingPackage the name of the package making the call.
- * @return true if the phone state is RINGING.
- */
- boolean isRingingForSubscriber(int subId, String callingPackage);
-
- /**
- * Check if an incoming phone call is ringing or call waiting.
- * @param callingPackage the name of the package making the call.
- * @return true if the phone state is RINGING.
- */
- boolean isRinging(String callingPackage);
-
- /**
- * Check if the phone is idle.
- * @param callingPackage the name of the package making the call.
- * @return true if the phone state is IDLE.
- */
- boolean isIdle(String callingPackage);
-
- /**
- * Check if the phone is idle on a particular subId.
- *
- * @param subId user preferred subId.
- * @param callingPackage the name of the package making the call.
- * @return true if the phone state is IDLE.
- */
- boolean isIdleForSubscriber(int subId, String callingPackage);
-
- /**
* Check to see if the radio is on or not.
* @param callingPackage the name of the package making the call.
* @return returns true if the radio is on.
@@ -865,14 +755,15 @@
* Ask the radio to connect to the input network and change selection mode to manual.
*
* @param subId the id of the subscription.
- * @param operatorNumeric the PLMN of the operator to attach to.
- * @param persistSelection Whether the selection will persist until reboot. If true, only allows
+ * @param operatorInfo the operator inforamtion, included the PLMN, long name and short name of
+ * the operator to attach to.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
* attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
* normal network selection next time.
- * @return true if the request suceeded.
+ * @return {@code true} on success; {@code true} on any failure.
*/
- boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,
- boolean persistSelection);
+ boolean setNetworkSelectionModeManual(
+ int subId, in OperatorInfo operatorInfo, boolean persisSelection);
/**
* Set the preferred network type.
@@ -1586,4 +1477,19 @@
* Return the network selection mode on the subscription with id {@code subId}.
*/
int getNetworkSelectionMode(int subId);
+
+ /**
+ * Get a list of SMS apps on a user.
+ */
+ String[] getSmsApps(int userId);
+
+ /**
+ * Get the default SMS app on a given user.
+ */
+ String getDefaultSmsApp(int userId);
+
+ /**
+ * Set the default SMS app to a given package on a given user.
+ */
+ void setDefaultSmsApp(int userId, String packageName);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 43d56b3..c03065c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -79,4 +79,5 @@
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
+ void notifyPreferredDataSubIdChanged(int preferredSubId);
}
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index d0245a0..a47e2b0 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -21,7 +21,7 @@
import android.os.Parcelable;
/**
- * {@hide}
+ * @hide
*/
public class OperatorInfo implements Parcelable {
public enum State {
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index f9de776..21f3b92 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -176,6 +176,10 @@
// FIXME: This is used to pass a subId via intents, we need to look at its usage, which is
// FIXME: extensive, and see if this should be an array of all active subId's or ...?
+ /**
+ * @Deprecated use {@link android.telephony.SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
+ * instead.
+ */
public static final String SUBSCRIPTION_KEY = "subscription";
public static final String SUB_SETTING = "subSettings";
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index d8ef429..39722c6 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -209,7 +209,14 @@
* Support smsto Uri scheme.
*/
public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
- int userId = getIncomingUserId(context);
+ return getApplicationCollectionAsUser(context, getIncomingUserId(context));
+ }
+
+ /**
+ * Same as {@link #getApplicationCollection} but it takes a target user ID.
+ */
+ public static Collection<SmsApplicationData> getApplicationCollectionAsUser(Context context,
+ int userId) {
final long token = Binder.clearCallingIdentity();
try {
return getApplicationCollectionInternal(context, userId);
@@ -535,13 +542,20 @@
* needs to have permission to set AppOps and write to secure settings.
*/
public static void setDefaultApplication(String packageName, Context context) {
+ setDefaultApplicationAsUser(packageName, context, getIncomingUserId(context));
+ }
+
+ /**
+ * Same as {@link #setDefaultApplication} but takes a target user id.
+ */
+ public static void setDefaultApplicationAsUser(String packageName, Context context,
+ int userId) {
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
if (!tm.isSmsCapable()) {
// No phone, no SMS
return;
}
- final int userId = getIncomingUserId(context);
final long token = Binder.clearCallingIdentity();
try {
setDefaultApplicationInternal(packageName, context, userId);
@@ -552,6 +566,8 @@
private static void setDefaultApplicationInternal(String packageName, Context context,
int userId) {
+ final UserHandle userHandle = UserHandle.of(userId);
+
// Get old package name
String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
@@ -628,7 +644,7 @@
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
}
- context.sendBroadcast(oldAppIntent);
+ context.sendBroadcastAsUser(oldAppIntent, userHandle);
}
// Notify the new sms app that it's now the default (if the new sms app has a receiver
// to handle the changed default sms intent).
@@ -646,8 +662,16 @@
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + packageName);
}
- context.sendBroadcast(intent);
+ context.sendBroadcastAsUser(intent, userHandle);
}
+
+ // Send an implicit broadcast for the system server.
+ // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ final Intent intent =
+ new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ context.sendBroadcastAsUser(intent, userHandle,
+ permission.MONITOR_DEFAULT_SMS_PACKAGE);
+
MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
applicationData.mPackageName);
}
@@ -799,7 +823,18 @@
* @return component name of the app and class to deliver SMS messages to
*/
public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
- int userId = getIncomingUserId(context);
+ return getDefaultSmsApplicationAsUser(context, updateIfNeeded, getIncomingUserId(context));
+ }
+
+ /**
+ * Gets the default SMS application on a given user
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @param userId target user ID.
+ * @return component name of the app and class to deliver SMS messages to
+ */
+ public static ComponentName getDefaultSmsApplicationAsUser(Context context,
+ boolean updateIfNeeded, int userId) {
final long token = Binder.clearCallingIdentity();
try {
ComponentName component = null;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 06378ba..23ea237 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -21,6 +21,7 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Binder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.Rlog;
@@ -307,4 +308,17 @@
Rlog.e(LOG_TAG, "Phone process is down, cannot check carrier privileges");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
+
+ /**
+ * Throws if the caller is not of a shell (or root) UID.
+ *
+ * @param callingUid pass Binder.callingUid().
+ */
+ public static void enforceShellOnly(int callingUid, String message) {
+ if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ return; // okay
+ }
+
+ throw new SecurityException(message + ": Only shell user can call it");
+ }
}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 0b8a02a..4d765d3 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -37,7 +37,8 @@
"junit.framework",
],
- droiddoc_options: ["stubsourceonly"],
+ droiddoc_options: ["-stubsourceonly"],
+ metalava_enabled: false,
compile_dex: true,
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 5eba017..37158e5 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -26,5 +26,6 @@
],
srcs_lib_whitelist_pkgs: ["android"],
+ metalava_enabled: false,
compile_dex: true,
}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index ea615b9..0a0d50c 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -40,7 +40,8 @@
"junit.textui",
],
- droiddoc_options: ["stubsourceonly"],
+ droiddoc_options: ["-stubsourceonly"],
+ metalava_enabled: false,
compile_dex: true
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 4a6fe49..976848c 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -106,9 +106,8 @@
private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
private static final String APP_LAUNCH_CMD = "am start -W -n";
private static final String SUCCESS_MESSAGE = "Status: ok";
- private static final String WARNING_MESSAGE = "Warning:";
+ private static final String TOTAL_TIME_MESSAGE = "TotalTime:";
private static final String COMPILE_SUCCESS = "Success";
- private static final String THIS_TIME = "ThisTime:";
private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION";
@@ -247,8 +246,14 @@
mIterationCycle = false;
// In the "applaunch.txt" file, trail launches is referenced using
// "TRIAL_LAUNCH"
- String appPkgName = mNameToIntent.get(launch.getApp())
- .getComponent().getPackageName();
+ Intent startIntent = mNameToIntent.get(launch.getApp());
+ if (startIntent == null) {
+ Log.w(TAG, "App does not exist: " + launch.getApp());
+ mResult.putString(mNameToResultKey.get(launch.getApp()),
+ "App does not exist");
+ continue;
+ }
+ String appPkgName = startIntent.getComponent().getPackageName();
if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
assertTrue(String.format("Not able to compile the app : %s", appPkgName),
compileApp(VERIFY_FILTER, appPkgName));
@@ -808,15 +813,13 @@
String launchTime = "-1";
String cpuCycles = "-1";
String majorFaults = "-1";
- boolean coldLaunchSuccess = false;
- boolean hotLaunchSuccess = false;
+ boolean launchSuccess = false;
try {
InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
/* SAMPLE OUTPUT : Cold launch
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Status: ok
Activity: com.google.android.calculator/com.android.calculator2.Calculator
- ThisTime: 357
TotalTime: 357
WaitTime: 377
Complete*/
@@ -825,7 +828,6 @@
Warning: Activity not started, its current task has been brought to the front
Status: ok
Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
- ThisTime: 60
TotalTime: 60
WaitTime: 67
Complete*/
@@ -836,54 +838,37 @@
Total test time,1.462129,seconds,*/
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
inputStream));
- String line = null;
- int lineCount = 1;
+ String line;
mBufferedWriter.newLine();
mBufferedWriter.write(headerInfo);
mBufferedWriter.newLine();
while ((line = bufferedReader.readLine()) != null) {
- if (lineCount == 2 && line.startsWith(SUCCESS_MESSAGE)) {
- coldLaunchSuccess = true;
+ mBufferedWriter.write(line);
+ mBufferedWriter.newLine();
+ if (line.startsWith(SUCCESS_MESSAGE)) {
+ launchSuccess = true;
}
- if (lineCount == 2 && line.startsWith(WARNING_MESSAGE)) {
- hotLaunchSuccess = true;
+ if (!launchSuccess) {
+ continue;
}
// Parse TotalTime which is the launch time
- if (coldLaunchSuccess && lineCount == 5) {
- String launchSplit[] = line.split(":");
- launchTime = launchSplit[1].trim();
- }
- if (hotLaunchSuccess && lineCount == 6) {
+ if (line.startsWith(TOTAL_TIME_MESSAGE)) {
String launchSplit[] = line.split(":");
launchTime = launchSplit[1].trim();
}
if (mSimplePerfAppOnly) {
- // Parse simpleperf output.
- if ((lineCount == 9 && coldLaunchSuccess)
- || (lineCount == 10 && hotLaunchSuccess)) {
- if (!line.contains("cpu-cycles")) {
- Log.e(TAG, "Error in simpleperf output");
- } else {
- cpuCycles = line.split(",")[0].trim();
- }
- } else if ((lineCount == 10 && coldLaunchSuccess)
- || (lineCount == 11 && hotLaunchSuccess)) {
- if (!line.contains("major-faults")) {
- Log.e(TAG, "Error in simpleperf output");
- } else {
- majorFaults = line.split(",")[0].trim();
- }
+ if (line.contains(",cpu-cycles,")) {
+ cpuCycles = line.split(",")[0].trim();
+ } else if (line.contains(",major-faults,")) {
+ majorFaults = line.split(",")[0].trim();
}
}
- mBufferedWriter.write(line);
- mBufferedWriter.newLine();
- lineCount++;
}
mBufferedWriter.flush();
inputStream.close();
} catch (IOException e) {
- Log.w(TAG, "Error writing the launch file", e);
+ Log.w(TAG, "Error parsing launch time and writing to file", e);
}
return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
}
diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DexLoggerIntegrationTests/AndroidManifest.xml
index a847e8f..a9f01ed 100644
--- a/tests/DexLoggerIntegrationTests/AndroidManifest.xml
+++ b/tests/DexLoggerIntegrationTests/AndroidManifest.xml
@@ -17,10 +17,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.dexloggertest">
- <!-- Tests feature introduced in P (27) -->
+ <!-- Tests feature introduced in P (28) -->
<uses-sdk
- android:minSdkVersion="27"
- android:targetSdkVersion="27" />
+ android:minSdkVersion="28"
+ android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.READ_LOGS" />
diff --git a/tests/ImfTest/Android.mk b/tests/ImfTest/Android.mk
deleted file mode 100644
index a8f5b08..0000000
--- a/tests/ImfTest/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := ImfTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/ImfTest/AndroidManifest.xml b/tests/ImfTest/AndroidManifest.xml
deleted file mode 100644
index 82dbe75..0000000
--- a/tests/ImfTest/AndroidManifest.xml
+++ /dev/null
@@ -1,146 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.imftest">
-
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
- <application>
-
- <activity android:name=".samples.InputTypeActivity" android:label="Input Type Activity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ButtonActivity" android:label="Button Activity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityNonScrollablePanScan" android:label="Big ET !Scroll Pan/Scan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ManyEditTextActivityNoScrollPanScan" android:label="ManyEditTextActivityNoScrollPanScan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityNonScrollableResize" android:label="Big ET !Scroll Resize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityScrollablePanScan" android:label="Big ET Scroll Pan/Scan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityScrollableResize" android:label="Big ET Scroll Resize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.EditTextActivityDialog" android:label="ET Dialog">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ManyEditTextActivityScrollPanScan" android:label="ManyEditTextActivityScrollPanScan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ManyEditTextActivityScrollResize" android:label="ManyEditTextActivityScrollResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BottomEditTextActivityPanScan" android:label="BottomEditTextActivityPanScan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BottomEditTextActivityResize" android:label="BottomEditTextActivityResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.OneEditTextActivitySelected" android:label="OneEditTextActivitySelected">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.OneEditTextActivityNotSelected" android:label="OneEditTextActivityNotSelected">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.AutoCompleteTextViewActivityPortrait" android:label="AutoCompleteTextViewActivityPortrait" android:screenOrientation="portrait">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.AutoCompleteTextViewActivityLandscape" android:label="AutoCompleteTextViewActivityLandscape" android:screenOrientation="landscape">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.DialogActivity" android:label="DialogActivity" android:screenOrientation="portrait">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- </application>
-
-</manifest>
diff --git a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml b/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml
deleted file mode 100644
index 1a2b7eb..0000000
--- a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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="match_parent"
- android:padding="20dip"
- android:orientation="vertical">
-
- <View
- android:id="@+id/blank"
- android:layout_height="0dip"
- android:layout_width="match_parent"
- android:layout_weight="1"/>
-
- <EditText
- android:id="@+id/dialog_edit_text"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:scrollHorizontally="true"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
-</LinearLayout>
diff --git a/tests/ImfTest/res/layout/full_screen_edit_text.xml b/tests/ImfTest/res/layout/full_screen_edit_text.xml
deleted file mode 100644
index e760ac1..0000000
--- a/tests/ImfTest/res/layout/full_screen_edit_text.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/samples/SampleCode/res/layout/baseline_1.xml
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<EditText xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/data"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:minLines="15"
- android:gravity="top"/>
-
diff --git a/tests/ImfTest/res/layout/one_edit_text_activity.xml b/tests/ImfTest/res/layout/one_edit_text_activity.xml
deleted file mode 100644
index 0558228..0000000
--- a/tests/ImfTest/res/layout/one_edit_text_activity.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/samples/SampleCode/res/layout/baseline_1.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:baselineAligned="false">
-
- <View android:id="@+id/blank"
- android:layout_height="0dip"
- android:layout_width="match_parent"
- android:layout_weight="1"
- />
-
- <EditText android:id="@+id/dialog_edit_text"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:scrollHorizontally="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@android:drawable/divider_horizontal_dark"
- />
-</LinearLayout>
diff --git a/tests/ImfTest/res/layout/sample_edit_text.xml b/tests/ImfTest/res/layout/sample_edit_text.xml
deleted file mode 100644
index 3ff6767..0000000
--- a/tests/ImfTest/res/layout/sample_edit_text.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/samples/SampleCode/res/layout/baseline_1.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:baselineAligned="false"
- android:gravity="center_vertical"
- >
-
- <TextView android:id="@+id/label"
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:gravity="right|center_vertical"
- />
-
- <EditText android:id="@+id/data"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dip"
- />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@android:drawable/divider_horizontal_dark"
- />
-</LinearLayout>
diff --git a/tests/ImfTest/res/values/config.xml b/tests/ImfTest/res/values/config.xml
deleted file mode 100644
index 5ae40a3..0000000
--- a/tests/ImfTest/res/values/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources>
- <bool name="def_expect_ime_autopop">false</bool>
-</resources>
diff --git a/tests/ImfTest/res/values/strings.xml b/tests/ImfTest/res/values/strings.xml
deleted file mode 100644
index fc87480..0000000
--- a/tests/ImfTest/res/values/strings.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Strings for sample activities -->
- <string name="normal_edit_text_label">Normal</string>
- <string name="uri_edit_text_label">Uri</string>
- <string name="email_address_edit_text_label">Email Address</string>
- <string name="email_subject_edit_text_label">Email Subject</string>
- <string name="email_content_edit_text_label">Email Content</string>
- <string name="person_name_edit_text_label">Person Name</string>
- <string name="postal_address_edit_text_label">Postal Address</string>
- <string name="password_edit_text_label">Password</string>
- <string name="search_string_edit_text_label">Search String</string>
- <string name="web_edit_text_label">Web Edit Text</string>
- <string name="signed_number_edit_text_label">Signed Number</string>
- <string name="decimal_number_edit_text_label">Decimal Number</string>
- <string name="phone_number_edit_text_label">Phone Number</string>
- <string name="normal_datetime_edit_text_label">Datetime</string>
- <string name="date_edit_text_label">Date</string>
- <string name="time_edit_text_label">Time</string>
- <string name="cap_chars_edit_text_label">Cap Chars</string>
- <string name="cap_words_edit_text_label">Cap Words</string>
- <string name="multiline_edit_text_label">Multiline</string>
- <string name="search_edit_text_label">Search (flag)</string>
- <string name="cap_sentences_edit_text_label">Cap Sentences</string>
- <string name="auto_complete_edit_text_label">Auto Complete</string>
- <string name="auto_correct_edit_text_label">Auto Correct</string>
- <string name="test_dialog">Test Dialog</string>
- <string name="open_dialog_scrollable">open scrollable dialog</string>
- <string name="open_dialog_nonscrollable">open nonscrollable dialog</string>
-
-
-</resources>
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
deleted file mode 100644
index 6115fd5..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.MediaStore;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.AutoCompleteTextView;
-import android.widget.ArrayAdapter;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-
-import com.android.internal.R;
-
-/*
- * Activity with AutoCompleteTextView forced to landscape mode
- */
-public class AutoCompleteTextViewActivityLandscape extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.auto_complete_list);
-
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_dropdown_item_1line, COUNTRIES);
- AutoCompleteTextView textView = findViewById(R.id.edit);
- textView.setAdapter(adapter);
- }
-
- static final String[] COUNTRIES = new String[] {
- "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
- "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
- "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
- "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
- "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
- "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
- "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
- "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
- "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
- "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
- "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
- "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
- "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
- "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
- "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
- "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
- "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
- "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
- "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
- "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
- "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
- "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
- "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
- "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
- "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
- "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
- "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
- "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
- "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
- "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
- "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
- "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
- "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
- "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
- "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
- "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
- "Ukraine", "United Arab Emirates", "United Kingdom",
- "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
- "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
- "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
- };
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
deleted file mode 100644
index 253c50f..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.AutoCompleteTextView;
-import android.widget.ArrayAdapter;
-
-import com.android.internal.R;
-
-/*
- * Activity with AutoCompleteTextView (Candidate bar should not appear)
- */
-public class AutoCompleteTextViewActivityPortrait extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.auto_complete_list);
-
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_dropdown_item_1line, COUNTRIES);
- AutoCompleteTextView textView = findViewById(R.id.edit);
- textView.setAdapter(adapter);
- }
-
- static final String[] COUNTRIES = new String[] {
- "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
- "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
- "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
- "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
- "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
- "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
- "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
- "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
- "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
- "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
- "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
- "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
- "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
- "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
- "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
- "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
- "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
- "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
- "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
- "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
- "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
- "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
- "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
- "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
- "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
- "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
- "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
- "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
- "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
- "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
- "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
- "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
- "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
- "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
- "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
- "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
- "Ukraine", "United Arab Emirates", "United Kingdom",
- "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
- "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
- "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
- };
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java
deleted file mode 100644
index 033082f..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-public class BigEditTextActivityNonScrollablePanScan extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false);
-
- ((LinearLayout) mRootView).addView(view);
-
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java
deleted file mode 100644
index 8a16dea..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-public class BigEditTextActivityNonScrollableResize extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false);
-
- ((LinearLayout) mRootView).addView(view);
-
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java
deleted file mode 100644
index b4fdc4c..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-
-public class BigEditTextActivityScrollablePanScan extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
- private LinearLayout mLayout;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
-
- mRootView = new ScrollView(this);
- ((ScrollView) mRootView).setFillViewport(true);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((ScrollView) mRootView), false);
-
- mLayout.addView(view);
-
- ((ScrollView) mRootView).addView(mLayout);
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java
deleted file mode 100644
index 757b6b5..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-
-public class BigEditTextActivityScrollableResize extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
- private LinearLayout mLayout;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-
- mRootView = new ScrollView(this);
- ((ScrollView) mRootView).setFillViewport(true);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((ScrollView) mRootView), false);
-
- mLayout.addView(view);
-
- ((ScrollView) mRootView).addView(mLayout);
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java
deleted file mode 100644
index 91a329d..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.imftest.R;
-
-/*
- * Activity with EditText at the bottom (Pan&Scan)
- */
-public class BottomEditTextActivityPanScan extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
-
- View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false);
- mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text);
- ((LinearLayout) mRootView).addView(view);
-
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java
deleted file mode 100644
index c4c41bc..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.imftest.R;
-
-/*
- * Activity with EditText at the bottom (Resize)
- */
-public class BottomEditTextActivityResize extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
-
- View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false);
- mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text);
- ((LinearLayout) mRootView).addView(view);
-
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
deleted file mode 100644
index dbaedf9..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-
-public class ButtonActivity extends Activity
-{
- static boolean mKeyboardIsActive = false;
- public static final int BUTTON_ID = 0;
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- final ButtonActivity instance = this;
-
- final Button myButton = new Button(this);
- myButton.setClickable(true);
- myButton.setText("Keyboard UP!");
- myButton.setId(BUTTON_ID);
- myButton.setFocusableInTouchMode(true);
- myButton.setOnClickListener(new View.OnClickListener()
- {
- public void onClick (View v)
- {
- InputMethodManager imm = InputMethodManager.getInstance();
- if (mKeyboardIsActive)
- {
- imm.hideSoftInputFromInputMethod(v.getWindowToken(), 0);
- myButton.setText("Keyboard UP!");
-
- }
- else
- {
- myButton.requestFocusFromTouch();
- imm.showSoftInput(v, 0);
- myButton.setText("Keyboard DOWN!");
- }
-
- mKeyboardIsActive = !mKeyboardIsActive;
- }
- });
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.addView(myButton);
- setContentView(layout);
- mRootView = layout;
- }
-
- public View getRootView() {
- return mRootView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java b/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java
deleted file mode 100644
index 3ed0386..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.Button;
-import android.view.LayoutInflater;
-import android.app.Dialog;
-
-public class DialogActivity extends Activity {
-
- private static final int DIALOG_WITHOUT_EDITTEXT = 0;
- private static final int DIALOG_WITH_EDITTEXT = 1;
-
- private LinearLayout mLayout;
- private LayoutInflater mInflater;
- private Button mButton1;
- private Button mButton2;
- private EditText mEditText;
-
-
- @Override
- protected void onCreate(Bundle icicle)
- {
- super.onCreate(icicle);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mButton1 = new Button(this);
- mButton1.setText("Dialog WITHOUT EditText");//(R.string.open_dialog_scrollable);
- mButton1.setOnClickListener(new View.OnClickListener()
- {
- public void onClick(View v)
- {
- showDialog(DIALOG_WITHOUT_EDITTEXT);
- }
- });
-
- mButton2 = new Button(this);
- mButton2.setText("Dialog WITH EditText");//(R.string.open_dialog_nonscrollable);
- mButton2.setOnClickListener(new View.OnClickListener()
- {
- public void onClick(View v)
- {
- showDialog(DIALOG_WITH_EDITTEXT);
- }
- });
-
- mEditText = new EditText(this);
- mLayout.addView(mEditText);
- mLayout.addView(mButton1);
- mLayout.addView(mButton2);
-
- setContentView(mLayout);
- }
-
- @Override
- protected Dialog onCreateDialog(int id)
- {
- switch (id)
- {
- case DIALOG_WITHOUT_EDITTEXT:
- return createDialog(false);
- case DIALOG_WITH_EDITTEXT:
- return createDialog(true);
- }
-
- return super.onCreateDialog(id);
- }
-
- protected Dialog createDialog(boolean bEditText)
- {
- LinearLayout layout;
- layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- if(bEditText)
- {
- EditText editText;
- editText = new EditText(this);
- layout.addView(editText);
- }
-
- Dialog d = new Dialog(this);
- d.setTitle("The DIALOG!!!");
- d.setCancelable(true);
- d.setContentView(layout);
- return d;
- }
-
- }
diff --git a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java
deleted file mode 100644
index 2591b7c..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-
-public class EditTextActivityDialog extends Activity {
-
- private static final int SCROLLABLE_DIALOG_ID = 0;
- private static final int NONSCROLLABLE_DIALOG_ID = 1;
-
- private LinearLayout mLayout;
- private ScrollView mScrollView;
- private LayoutInflater mInflater;
- private Button mButton1;
- private Button mButton2;
-
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mButton1 = new Button(this);
- mButton1.setText(R.string.open_dialog_scrollable);
- mButton1.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- showDialog(SCROLLABLE_DIALOG_ID);
- }
- });
-
- mButton2 = new Button(this);
- mButton2.setText(R.string.open_dialog_nonscrollable);
- mButton2.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- showDialog(NONSCROLLABLE_DIALOG_ID);
- }
- });
-
- mLayout.addView(mButton1);
- mLayout.addView(mButton2);
-
- setContentView(mLayout);
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case SCROLLABLE_DIALOG_ID:
- return createDialog(true);
- case NONSCROLLABLE_DIALOG_ID:
- return createDialog(false);
- }
-
- return super.onCreateDialog(id);
- }
-
- protected Dialog createDialog(boolean scrollable) {
- View layout;
- EditText editText;
-
- if (scrollable) {
- layout = new ScrollView(EditTextActivityDialog.this);
- ((ScrollView) layout).setMinimumHeight(mLayout.getHeight());
-
- ((ScrollView) layout).addView((
- LinearLayout) View.inflate(EditTextActivityDialog.this,
- R.layout.dialog_edit_text_no_scroll, null));
- } else {
- layout = View.inflate(EditTextActivityDialog.this,
- R.layout.dialog_edit_text_no_scroll, null);
- }
-
- Dialog d = new Dialog(EditTextActivityDialog.this);
- d.setTitle(getString(R.string.test_dialog));
- d.setCancelable(true);
- d.setContentView(layout);
- return d;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java b/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java
deleted file mode 100644
index 299e6bb..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-
-public class InputTypeActivity extends Activity {
-
- private LinearLayout mLayout;
- private ScrollView mScrollView;
- private LayoutInflater mInflater;
- private ViewGroup mParent;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mScrollView = new ScrollView(this);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mInflater = getLayoutInflater();
- mParent = mLayout;
-
- /* Normal Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL,
- R.string.normal_edit_text_label));
-
- /* Normal Edit Text w/Cap Chars Flag*/
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS,
- R.string.cap_chars_edit_text_label));
-
- /* Normal Edit Text w/Cap Words Flag*/
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS,
- R.string.cap_words_edit_text_label));
-
- /* Normal Edit Text w/Cap Multiline Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE,
- R.string.multiline_edit_text_label));
-
- /* Normal Edit Text w/Cap Sentences Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES,
- R.string.cap_sentences_edit_text_label));
-
- /* Normal Edit Text w/Auto-complete Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE,
- R.string.auto_complete_edit_text_label));
-
- /* Normal Edit Text w/Auto-correct Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT,
- R.string.auto_correct_edit_text_label));
-
- /* Uri Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_URI,
- R.string.uri_edit_text_label));
-
- /* Email Address Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
- R.string.email_address_edit_text_label));
-
- /* Email Subject Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT,
- R.string.email_subject_edit_text_label));
-
- /* Email Content Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE,
- R.string.email_content_edit_text_label));
-
- /* Person Name Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME,
- R.string.person_name_edit_text_label));
-
- /* Postal Address Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS,
- R.string.postal_address_edit_text_label));
-
- /* Password Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PASSWORD,
- R.string.password_edit_text_label));
-
- /* Web Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT,
- R.string.web_edit_text_label));
-
- /* Signed Number Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_NUMBER|EditorInfo.TYPE_NUMBER_FLAG_SIGNED,
- R.string.signed_number_edit_text_label));
-
- /* Decimal Number Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_NUMBER|EditorInfo.TYPE_NUMBER_FLAG_DECIMAL,
- R.string.decimal_number_edit_text_label));
-
- /* Phone Number Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_PHONE,
- R.string.phone_number_edit_text_label));
-
- /* Normal Datetime Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_NORMAL,
- R.string.normal_datetime_edit_text_label));
-
- /* Date Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_DATE,
- R.string.date_edit_text_label));
-
- /* Time Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_TIME,
- R.string.time_edit_text_label));
-
- mScrollView.addView(mLayout);
- setContentView(mScrollView);
- }
-
- private View buildEntryView(int inputType, int label) {
-
-
- View view = mInflater.inflate(R.layout.sample_edit_text, mParent, false);
-
- EditText editText = (EditText) view.findViewById(R.id.data);
- editText.setInputType(inputType);
-
- TextView textView = (TextView) view.findViewById(R.id.label);
- textView.setText(label);
-
- return view;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java
deleted file mode 100644
index 646e480..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Full screen of EditTexts (Non-Scrollable, Pan&Scan)
- */
-public class ManyEditTextActivityNoScrollPanScan extends Activity
-{
- public static final int NUM_EDIT_TEXTS = 9;
-
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
-
- for (int i=0; i<NUM_EDIT_TEXTS; i++)
- {
- final EditText editText = new EditText(this);
- editText.setText(String.valueOf(i));
- editText.setId(i);
- ((LinearLayout) mRootView).addView(editText);
- }
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java
deleted file mode 100644
index 0387e1e..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Full screen of EditTexts (Scrollable, Pan&Scan)
- */
-public class ManyEditTextActivityScrollPanScan extends Activity
-{
- public static final int NUM_EDIT_TEXTS = 12;
-
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- mRootView = new ScrollView(this);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- for (int i=0; i<NUM_EDIT_TEXTS; i++)
- {
- final EditText editText = new EditText(this);
- editText.setText(String.valueOf(i));
- editText.setId(i);
- layout.addView(editText);
- }
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java
deleted file mode 100644
index 7793b55..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.ScrollView;
-
-/*
- * Full screen of EditTexts (Scrollable, Resize)
- */
-public class ManyEditTextActivityScrollResize extends Activity
-{
- public static final int NUM_EDIT_TEXTS = 12;
-
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- mRootView = new ScrollView(this);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- for (int i=0; i<NUM_EDIT_TEXTS; i++)
- {
- final EditText editText = new EditText(this);
- editText.setText(String.valueOf(i));
- editText.setId(i);
- layout.addView(editText);
- }
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java
deleted file mode 100644
index c4be21c..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Debug;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Activity with non-EditText view selected initially
- */
-public class OneEditTextActivityNotSelected extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- mRootView = new ScrollView(this);
-
- EditText editText = new EditText(this);
- Button button = new Button(this);
- button.setText("The focus is here.");
- button.setFocusableInTouchMode(true);
- button.requestFocus();
- mDefaultFocusedView = button;
- layout.addView(button);
- layout.addView(editText);
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java
deleted file mode 100644
index 64882aa..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Activity with EditText selected initially
- */
-public class OneEditTextActivitySelected extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- mRootView = new ScrollView(this);
-
- EditText editText = new EditText(this);
- editText.requestFocus();
- mDefaultFocusedView = editText;
- layout.addView(editText);
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
-
- // set to resize so IME is always shown (and also so
- // ImfBaseTestCase#destructiveCheckImeInitialState thinks it should always be shown
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk
deleted file mode 100644
index 14186d7..0000000
--- a/tests/ImfTest/tests/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-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-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-LOCAL_PACKAGE_NAME := ImfTestTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_INSTRUMENTATION_FOR := ImfTest
-
-include $(BUILD_PACKAGE)
diff --git a/tests/ImfTest/tests/AndroidManifest.xml b/tests/ImfTest/tests/AndroidManifest.xml
deleted file mode 100644
index c02fa0b..0000000
--- a/tests/ImfTest/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.imftest.tests">
-
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <!--
- This declares that this app uses the instrumentation test runner targeting
- the package of com.android.imftest. To run the tests use the command:
- "adb shell am instrument -w com.android.imftest.tests/android.test.InstrumentationTestRunner"
- -->
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.imftest"
- android:label="imf tests"/>
-
-</manifest>
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java
deleted file mode 100644
index 2db11c5..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityNonScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityNonScrollablePanScan> {
-
- public final String TAG = "BigEditTextActivityNonScrollablePanScanTests";
-
- public BigEditTextActivityNonScrollablePanScanTests() {
- super(BigEditTextActivityNonScrollablePanScan.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java
deleted file mode 100644
index 1050794..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityNonScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityNonScrollableResize> {
-
- public final String TAG = "BigEditTextActivityNonScrollableResizeTests";
-
- public BigEditTextActivityNonScrollableResizeTests() {
- super(BigEditTextActivityNonScrollableResize.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java
deleted file mode 100644
index 1e848b0..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityScrollablePanScan> {
-
- public final String TAG = "BigEditTextActivityScrollablePanScanTests";
-
- public BigEditTextActivityScrollablePanScanTests() {
- super(BigEditTextActivityScrollablePanScan.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java
deleted file mode 100644
index de607d6..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityScrollableResize> {
-
- public final String TAG = "BigEditTextActivityScrollableResizeTests";
-
- public BigEditTextActivityScrollableResizeTests() {
- super(BigEditTextActivityScrollableResize.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityScrollableResize) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityScrollableResize) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java
deleted file mode 100644
index c521905..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BottomEditTextActivityPanScanTests extends ImfBaseTestCase<BottomEditTextActivityPanScan> {
-
- public final String TAG = "BottomEditTextActivityPanScanTests";
-
- public BottomEditTextActivityPanScanTests() {
- super(BottomEditTextActivityPanScan.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BottomEditTextActivityPanScan) mTargetActivity).getRootView();
- View servedView = ((BottomEditTextActivityPanScan) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java
deleted file mode 100644
index 9a69fd5..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BottomEditTextActivityResizeTests extends ImfBaseTestCase<BottomEditTextActivityResize> {
-
- public final String TAG = "BottomEditTextActivityResizeTests";
-
- public BottomEditTextActivityResizeTests() {
- super(BottomEditTextActivityResize.class);
- }
-
- @LargeTest
- public void testAppAdjustmentResize() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BottomEditTextActivityResize) mTargetActivity).getRootView();
- View servedView = ((BottomEditTextActivityResize) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java
deleted file mode 100644
index f6f97b5..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.KeyEvent;
-import android.widget.Button;
-
-
-public class ButtonActivityTest extends ImfBaseTestCase<ButtonActivity> {
-
- final public String TAG = "ButtonActivityTest";
-
- public ButtonActivityTest() {
- super(ButtonActivity.class);
- }
-
- @LargeTest
- public void testButtonActivatesIme() {
-
- final Button button = (Button) mTargetActivity.findViewById(ButtonActivity.BUTTON_ID);
-
- // Push button
- // Bring the target EditText into focus.
- mTargetActivity.runOnUiThread(new Runnable() {
- public void run() {
- button.requestFocus();
- }
- });
-
- sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-
- // Give it a couple seconds
- pause(2000);
-
- // We should have initialized imm.mServedView and imm.mCurrentTextBoxAttribute
- assertTrue(mImm.isActive());
- // imm.mServedInputConnection should be null since Button doesn't override onCreateInputConnection().
- assertFalse(mImm.isAcceptingText());
-
- destructiveCheckImeInitialState(mTargetActivity.getRootView(), button);
-
- }
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
deleted file mode 100644
index 32f80a3..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.imftest.R;
-
-public abstract class ImfBaseTestCase<T extends Activity> extends InstrumentationTestCase {
-
- /*
- * The amount of time we are willing to wait for the IME to appear after a user action
- * before we give up and fail the test.
- */
- public final long WAIT_FOR_IME = 5000;
-
- /*
- * Unfortunately there is now way for us to know how tall the IME is,
- * so we have to hard code a minimum and maximum value.
- */
- public final int IME_MIN_HEIGHT = 150;
- public final int IME_MAX_HEIGHT = 300;
-
- protected InputMethodManager mImm;
- protected T mTargetActivity;
- protected boolean mExpectAutoPop;
- private Class<T> mTargetActivityClass;
-
- public ImfBaseTestCase(Class<T> activityClass) {
- mTargetActivityClass = activityClass;
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- final String packageName = getInstrumentation().getTargetContext().getPackageName();
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- mTargetActivity = launchActivityWithIntent(packageName, mTargetActivityClass, intent);
- // expect ime to auto pop up if device has no hard keyboard
- int keyboardType = mTargetActivity.getResources().getConfiguration().keyboard;
- mExpectAutoPop = (keyboardType == Configuration.KEYBOARD_NOKEYS ||
- keyboardType == Configuration.KEYBOARD_UNDEFINED);
-
- mImm = InputMethodManager.getInstance();
-
- KeyguardManager keyguardManager =
- (KeyguardManager) getInstrumentation().getContext().getSystemService(
- Context.KEYGUARD_SERVICE);
- keyguardManager.newKeyguardLock("imftest").disableKeyguard();
- }
-
- // Utility test methods
- public void verifyEditTextAdjustment(final View editText, int rootViewHeight) {
-
- int[] origLocation = new int[2];
- int[] newLocation = new int[2];
-
- // Tell the keyboard to go away.
- mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
-
- // Bring the target EditText into focus.
- mTargetActivity.runOnUiThread(new Runnable() {
- public void run() {
- editText.requestFocus();
- }
- });
-
- // Get the original location of the EditText.
- editText.getLocationOnScreen(origLocation);
-
- // Tap the EditText to bring up the IME.
- sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-
- // Wait until the EditText pops above the IME or until we hit the timeout.
- editText.getLocationOnScreen(newLocation);
- long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME;
- while (newLocation[1] > rootViewHeight - IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) {
- editText.getLocationOnScreen(newLocation);
- pause(100);
- }
-
- assertTrue(newLocation[1] <= rootViewHeight - IME_MIN_HEIGHT);
-
- // Tell the keyboard to go away.
- mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
- }
-
- public void destructiveCheckImeInitialState(View rootView, View servedView) {
- int windowSoftInputMode = mTargetActivity.getWindow().getAttributes().softInputMode;
- int adjustMode = windowSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
- if (mExpectAutoPop && adjustMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
- assertTrue(destructiveCheckImeUp(rootView, servedView));
- } else {
- assertFalse(destructiveCheckImeUp(rootView, servedView));
- }
- }
-
- public boolean destructiveCheckImeUp(View rootView, View servedView) {
- int origHeight;
- int newHeight;
-
- origHeight = rootView.getHeight();
-
- // Tell the keyboard to go away.
- mImm.hideSoftInputFromWindow(servedView.getWindowToken(), 0);
-
- // Give it five seconds to adjust
- newHeight = rootView.getHeight();
- long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME;
- while (Math.abs(newHeight - origHeight) < IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) {
- newHeight = rootView.getHeight();
- }
-
- return (Math.abs(origHeight - newHeight) >= IME_MIN_HEIGHT);
- }
-
- void pause(int millis) {
- try {
- Thread.sleep(millis);
- } catch (InterruptedException e) {
- }
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java
deleted file mode 100644
index 278efb1..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.widget.EditText;
-
-
-public abstract class ManyEditTextActivityBaseTestCase<T extends Activity> extends ImfBaseTestCase<T> {
-
- public ManyEditTextActivityBaseTestCase(Class<T> activityClass){
- super(activityClass);
- }
-
- public abstract void testAllEditTextsAdjust();
-
- public void verifyAllEditTextAdjustment(int numEditTexts, int rootViewHeight) {
-
- for (int i = 0; i < numEditTexts; i++) {
- final EditText lastEditText = (EditText) mTargetActivity.findViewById(i);
- verifyEditTextAdjustment(lastEditText, rootViewHeight);
- }
-
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java
deleted file mode 100644
index 4f8d14e..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-
-
-public class ManyEditTextActivityNoScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityNoScrollPanScan> {
-
- public final String TAG = "ManyEditTextActivityNoScrollPanScanTests";
-
- public ManyEditTextActivityNoScrollPanScanTests() {
- super(ManyEditTextActivityNoScrollPanScan.class);
- }
-
-
- @LargeTest
- public void testAllEditTextsAdjust() {
- verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS,
- mTargetActivity.getRootView().getMeasuredHeight());
- }
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java
deleted file mode 100644
index 7f98f7f..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-
-
-public class ManyEditTextActivityScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollPanScan> {
-
- public final String TAG = "ManyEditTextActivityScrollPanScanTests";
-
-
- public ManyEditTextActivityScrollPanScanTests() {
- super(ManyEditTextActivityScrollPanScan.class);
- }
-
- @LargeTest
- public void testAllEditTextsAdjust() {
- verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS,
- mTargetActivity.getRootView().getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java
deleted file mode 100644
index 68dae87..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-
-
-public class ManyEditTextActivityScrollResizeTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollResize> {
-
- public final String TAG = "ManyEditTextActivityScrollResizeTests";
-
-
- public ManyEditTextActivityScrollResizeTests() {
- super(ManyEditTextActivityScrollResize.class);
- }
-
- @LargeTest
- public void testAllEditTextsAdjust() {
- verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS,
- mTargetActivity.getRootView().getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java
deleted file mode 100644
index 6147d3c..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-
-public class OneEditTextActivityNotSelectedTests extends ImfBaseTestCase<OneEditTextActivityNotSelected> {
-
- public final String TAG = "OneEditTextActivityNotSelectedTests";
-
- public OneEditTextActivityNotSelectedTests() {
- super(OneEditTextActivityNotSelected.class);
- }
-
- @LargeTest
- public void testSoftKeyboardNoAutoPop() {
-
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- assertFalse(mImm.isAcceptingText());
-
- View rootView = ((OneEditTextActivityNotSelected) mTargetActivity).getRootView();
- View servedView = ((OneEditTextActivityNotSelected) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java
deleted file mode 100644
index 42fcd66..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.KeyEvent;
-import android.view.View;
-
-
-public class OneEditTextActivitySelectedTests extends ImfBaseTestCase<OneEditTextActivitySelected> {
-
- public final String TAG = "OneEditTextActivitySelectedTests";
-
- public OneEditTextActivitySelectedTests() {
- super(OneEditTextActivitySelected.class);
- }
-
- @LargeTest
- public void testSoftKeyboardAutoPop() {
-
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- assertTrue(mImm.isAcceptingText());
-
- View rootView = ((OneEditTextActivitySelected) mTargetActivity).getRootView();
- View servedView = ((OneEditTextActivitySelected) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/NativeProcessesMemoryTest/Android.bp b/tests/NativeProcessesMemoryTest/Android.bp
new file mode 100644
index 0000000..f2625bf
--- /dev/null
+++ b/tests/NativeProcessesMemoryTest/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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_test_host {
+ name: "native-processes-memory-test",
+ srcs: ["src/**/*.java"],
+
+ libs: ["tradefed"],
+ test_suites: ["general-tests"],
+}
diff --git a/media/lib/remotedisplay/com.android.media.remotedisplay.xml b/tests/NativeProcessesMemoryTest/AndroidTest.xml
similarity index 60%
rename from media/lib/remotedisplay/com.android.media.remotedisplay.xml
rename to tests/NativeProcessesMemoryTest/AndroidTest.xml
index 77a91d2..0f326fd 100644
--- a/media/lib/remotedisplay/com.android.media.remotedisplay.xml
+++ b/tests/NativeProcessesMemoryTest/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<permissions>
- <library name="com.android.media.remotedisplay"
- file="/system/framework/com.android.media.remotedisplay.jar" />
-</permissions>
+<configuration description="Runs the native processes memory tests">
+ <option name="test-suite-tag" value="native-processes-memory-test" />
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.tests.nativeprocesses.NativeProcessesMemoryTest" />
+ </test>
+</configuration>
diff --git a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
new file mode 100644
index 0000000..c86f06e
--- /dev/null
+++ b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.nativeprocesses;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ByteArrayInputStreamSource;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.InputMismatchException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Scanner;
+
+/**
+ * Test to gather native processes count and memory usage.
+ *
+ * Native processes are parsed from dumpsys meminfo --oom -c
+ *
+ * <pre>
+ * time,35857009,35857009
+ * oom,native,331721,N/A
+ * proc,native,init,1,2715,N/A,e
+ * proc,native,init,445,1492,N/A,e
+ * ...
+ * </pre>
+ *
+ * For each native process we also look at its showmap output, and gather the PSS, VSS, and RSS.
+ * The showmap output is also saved to test logs.
+ *
+ * The metrics reported are:
+ *
+ * <pre>
+ * - number of native processes
+ * - total memory use of native processes
+ * - memory usage of each native process (one measurement for each process)
+ * </pre>
+ */
+public class NativeProcessesMemoryTest implements IDeviceTest, IRemoteTest {
+ // the dumpsys process comes and go as we run this test, changing pids, so ignore it
+ private static final List<String> PROCESSES_TO_IGNORE = Arrays.asList("dumpsys");
+
+ // -c gives us a compact output which is comma separated
+ private static final String DUMPSYS_MEMINFO_OOM_CMD = "dumpsys meminfo --oom -c";
+
+ private static final String SEPARATOR = ",";
+ private static final String LINE_SEPARATOR = "\\n";
+
+ // name of this test run, used for reporting
+ private static final String RUN_NAME = "NativeProcessesTest";
+ // key used to report the number of native processes
+ private static final String NUM_NATIVE_PROCESSES_KEY = "Num_native_processes";
+
+ private final Map<String, String> mNativeProcessToMemory = new HashMap<String, String>();
+ // identity for summing over MemoryMetric
+ private final MemoryMetric mZero = new MemoryMetric(0, 0, 0);
+
+ private ITestDevice mTestDevice;
+ private ITestInvocationListener mListener;
+
+ public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+ mListener = listener;
+ // showmap requires root, we enable it here for the rest of the test
+ mTestDevice.enableAdbRoot();
+
+ listener.testRunStarted(RUN_NAME, 1 /* testCount */);
+
+ TestDescription testDescription = new TestDescription(getClass().getName(), "run");
+ listener.testStarted(testDescription);
+
+ // process name -> list of pids with that name
+ Map<String, List<String>> nativeProcesses = collectNativeProcesses();
+ sampleAndLogAllProcesses(nativeProcesses);
+
+ // want to also record the number of native processes
+ mNativeProcessToMemory.put(
+ NUM_NATIVE_PROCESSES_KEY, Integer.toString(nativeProcesses.size()));
+
+ listener.testEnded(testDescription, mNativeProcessToMemory);
+ listener.testRunEnded(0, new HashMap<String, String>());
+ }
+
+ /** Samples memory of all processes and logs the memory use. */
+ private void sampleAndLogAllProcesses(Map<String, List<String>> nativeProcesses)
+ throws DeviceNotAvailableException {
+ for (Map.Entry<String, List<String>> entry : nativeProcesses.entrySet()) {
+ String processName = entry.getKey();
+ if (PROCESSES_TO_IGNORE.contains(processName)) {
+ continue;
+ }
+
+ // for all pids associated with this process name, record their memory usage
+ List<MemoryMetric> allMemsForProcess = new ArrayList<>();
+ for (String pid : entry.getValue()) {
+ Optional<MemoryMetric> mem = snapMemoryUsage(processName, pid);
+ if (mem.isPresent()) {
+ allMemsForProcess.add(mem.get());
+ }
+ }
+
+ // if for some reason a process does not have any MemoryMetric, don't log it
+ if (allMemsForProcess.isEmpty()) {
+ continue;
+ }
+
+ // combine all the memory metrics of process with the same name
+ MemoryMetric combined = allMemsForProcess.stream().reduce(mZero, MemoryMetric::sum);
+ logMemoryMetric(processName, combined);
+ }
+ }
+
+ @Override
+ public void setDevice(ITestDevice device) {
+ mTestDevice = device;
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mTestDevice;
+ }
+
+ /**
+ * Query system for a list of native process.
+ *
+ * @return a map from process name to a list of pids that share the same name
+ */
+ private Map<String, List<String>> collectNativeProcesses() throws DeviceNotAvailableException {
+ HashMap<String, List<String>> nativeProcesses = new HashMap<>();
+ String memInfo = mTestDevice.executeShellCommand(DUMPSYS_MEMINFO_OOM_CMD);
+
+ for (String line : memInfo.split(LINE_SEPARATOR)) {
+ String[] splits = line.split(SEPARATOR);
+ // ignore lines that don't list a native process
+ if (splits.length < 4 || !splits[0].equals("proc") || !splits[1].equals("native")) {
+ continue;
+ }
+
+ String processName = splits[2];
+ String pid = splits[3];
+ if (nativeProcesses.containsKey(processName)) {
+ nativeProcesses.get(processName).add(pid);
+ } else {
+ nativeProcesses.put(processName, new ArrayList<>(Arrays.asList(pid)));
+ }
+ }
+ return nativeProcesses;
+ }
+
+ /** Logs an entire showmap output to the test logs. */
+ private void logShowmap(String label, String showmap) {
+ try (ByteArrayInputStreamSource source =
+ new ByteArrayInputStreamSource(showmap.getBytes())) {
+ mListener.testLog(label + "_showmap", LogDataType.TEXT, source);
+ }
+ }
+
+ /**
+ * Extract VSS, PSS, and RSS from showmap of a process.
+ * The showmap output is also added to test logs.
+ */
+ private Optional<MemoryMetric> snapMemoryUsage(String processName, String pid)
+ throws DeviceNotAvailableException {
+ // TODO(zhin): copied from com.android.tests.sysmem.host.Metrics#sample(), extract?
+ String showmap = mTestDevice.executeShellCommand("showmap " + pid);
+ logShowmap(processName + "_" + pid, showmap);
+
+ // CHECKSTYLE:OFF Generated code
+ // The last lines of the showmap output looks like:
+ // ------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
+ // virtual shared shared private private
+ // size RSS PSS clean dirty clean dirty swap swapPSS # object
+ // -------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
+ // 12848 4240 1543 2852 64 36 1288 0 0 171 TOTAL
+ // CHECKSTYLE:ON Generated code
+ try {
+ int pos = showmap.lastIndexOf("----");
+ Scanner sc = new Scanner(showmap.substring(pos));
+ sc.next();
+ long vss = sc.nextLong();
+ long rss = sc.nextLong();
+ long pss = sc.nextLong();
+ return Optional.of(new MemoryMetric(pss, rss, vss));
+ } catch (InputMismatchException e) {
+ // this might occur if we have transient processes, it was collected earlier,
+ // but by the time we look at showmap the process is gone
+ CLog.e("Unable to parse MemoryMetric from showmap of pid: " + pid + " processName: "
+ + processName);
+ CLog.e(showmap);
+ return Optional.empty();
+ }
+ }
+
+ /** Logs a MemoryMetric of a process. */
+ private void logMemoryMetric(String processName, MemoryMetric memoryMetric) {
+ mNativeProcessToMemory.put(processName + "_pss", Long.toString(memoryMetric.pss));
+ mNativeProcessToMemory.put(processName + "_rss", Long.toString(memoryMetric.rss));
+ mNativeProcessToMemory.put(processName + "_vss", Long.toString(memoryMetric.vss));
+ }
+
+ /** Container of memory numbers we want to log. */
+ private final class MemoryMetric {
+ final long pss;
+ final long rss;
+ final long vss;
+
+ MemoryMetric(long pss, long rss, long vss) {
+ this.pss = pss;
+ this.rss = rss;
+ this.vss = vss;
+ }
+
+ MemoryMetric sum(MemoryMetric other) {
+ return new MemoryMetric(
+ pss + other.pss, rss + other.rss, vss + other.vss);
+ }
+ }
+}
diff --git a/tests/RemoteDisplayProvider/Android.mk b/tests/RemoteDisplayProvider/Android.mk
index e827ec2..43bf024 100644
--- a/tests/RemoteDisplayProvider/Android.mk
+++ b/tests/RemoteDisplayProvider/Android.mk
@@ -18,9 +18,9 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := RemoteDisplayProviderTest
LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res
-LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay.stubs
+LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index ba1a2ea..6dae3f1 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.NETWORK_STACK" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
new file mode 100644
index 0000000..122edba
--- /dev/null
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_RCVTIMEO;
+
+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.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.netlink.StructNlMsgHdr;
+import android.os.Process;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
+import android.system.Os;
+import android.system.StructTimeval;
+import android.util.Log;
+import java.io.FileDescriptor;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import libcore.util.HexEncoding;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InetDiagSocketTest {
+ private final String TAG = "InetDiagSocketTest";
+ private ConnectivityManager mCm;
+ private Context mContext;
+ private final static int SOCKET_TIMEOUT_MS = 100;
+
+ @Before
+ public void setUp() throws Exception {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = instrumentation.getTargetContext();
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ private class Connection {
+ public int socketDomain;
+ public int socketType;
+ public InetAddress localAddress;
+ public InetAddress remoteAddress;
+ public InetAddress localhostAddress;
+ public InetSocketAddress local;
+ public InetSocketAddress remote;
+ public int protocol;
+ public FileDescriptor localFd;
+ public FileDescriptor remoteFd;
+
+ public FileDescriptor createSocket() throws Exception {
+ return Os.socket(socketDomain, socketType, protocol);
+ }
+
+ public Connection(String to, String from) throws Exception {
+ remoteAddress = InetAddress.getByName(to);
+ if (from != null) {
+ localAddress = InetAddress.getByName(from);
+ } else {
+ localAddress = (remoteAddress instanceof Inet4Address) ?
+ Inet4Address.getByName("localhost") : Inet6Address.getByName("::");
+ }
+ if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) {
+ socketDomain = AF_INET;
+ localhostAddress = Inet4Address.getByName("localhost");
+ } else {
+ socketDomain = AF_INET6;
+ localhostAddress = Inet6Address.getByName("::");
+ }
+ }
+
+ public void close() throws Exception {
+ Os.close(localFd);
+ }
+ }
+
+ private class TcpConnection extends Connection {
+ public TcpConnection(String to, String from) throws Exception {
+ super(to, from);
+ protocol = IPPROTO_TCP;
+ socketType = SOCK_STREAM;
+
+ remoteFd = createSocket();
+ Os.bind(remoteFd, remoteAddress, 0);
+ Os.listen(remoteFd, 10);
+ int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort();
+
+ localFd = createSocket();
+ Os.bind(localFd, localAddress, 0);
+ Os.connect(localFd, remoteAddress, remotePort);
+
+ local = (InetSocketAddress) Os.getsockname(localFd);
+ remote = (InetSocketAddress) Os.getpeername(localFd);
+ }
+
+ public void close() throws Exception {
+ super.close();
+ Os.close(remoteFd);
+ }
+ }
+ private class UdpConnection extends Connection {
+ public UdpConnection(String to, String from) throws Exception {
+ super(to, from);
+ protocol = IPPROTO_UDP;
+ socketType = SOCK_DGRAM;
+
+ remoteFd = null;
+ localFd = createSocket();
+ Os.bind(localFd, localAddress, 0);
+
+ Os.connect(localFd, remoteAddress, 7);
+ local = (InetSocketAddress) Os.getsockname(localFd);
+ remote = new InetSocketAddress(remoteAddress, 7);
+ }
+ }
+
+ private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, boolean expectSuccess) {
+ final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID;
+ final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
+ assertEquals(expectedUid, uid);
+ }
+
+ private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
+ UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(),
+ conn.localAddress.getHostAddress());
+ final int localPort = udp.local.getPort();
+ udp.close();
+ return localPort;
+ }
+
+ public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
+ /**
+ * For TCP connections, create a test connection and verify that this
+ * {protocol, local, remote} socket result in receiving a valid UID.
+ */
+ TcpConnection tcp = new TcpConnection(to, from);
+ checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
+ checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
+ checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false);
+ checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
+ tcp.close();
+
+ /**
+ * For UDP connections, either a complete match {protocol, local, remote} or a
+ * partial match {protocol, local} should return a valid UID.
+ */
+ UdpConnection udp = new UdpConnection(to,from);
+ checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
+ checkConnectionOwnerUid(udp.protocol, udp.local, new InetSocketAddress(0), true);
+ checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
+ checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
+ udp.remote, false);
+ udp.close();
+ }
+
+ @Test
+ public void testGetConnectionOwnerUid() throws Exception {
+ checkGetConnectionOwnerUid("::", null);
+ checkGetConnectionOwnerUid("::", "::");
+ checkGetConnectionOwnerUid("0.0.0.0", null);
+ checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0");
+ checkGetConnectionOwnerUid("127.0.0.1", null);
+ checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2");
+ checkGetConnectionOwnerUid("::1", null);
+ checkGetConnectionOwnerUid("::1", "::1");
+ }
+
+ // Hexadecimal representation of InetDiagReqV2 request.
+ private static final String INET_DIAG_REQ_V2_UDP_INET4_HEX =
+ // struct nlmsghdr
+ "48000000" + // length = 72
+ "1400" + // type = SOCK_DIAG_BY_FAMILY
+ "0103" + // flags = NLM_F_REQUEST | NLM_F_DUMP
+ "00000000" + // seqno
+ "00000000" + // pid (0 == kernel)
+ // struct inet_diag_req_v2
+ "02" + // family = AF_INET
+ "11" + // protcol = IPPROTO_UDP
+ "00" + // idiag_ext
+ "00" + // pad
+ "ffffffff" + // idiag_states
+ // inet_diag_sockid
+ "a5de" + // idiag_sport = 42462
+ "b971" + // idiag_dport = 47473
+ "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2
+ "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
+ "00000000" + // idiag_if
+ "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
+ private static final byte[] INET_DIAG_REQ_V2_UDP_INET4_BYTES =
+ HexEncoding.decode(INET_DIAG_REQ_V2_UDP_INET4_HEX.toCharArray(), false);
+
+ @Test
+ public void testInetDiagReqV2UdpInet4() throws Exception {
+ InetSocketAddress local = new InetSocketAddress(InetAddress.getByName("10.0.100.2"),
+ 42462);
+ InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
+ 47473);
+ final byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_UDP, local, remote, AF_INET,
+ (short) (NLM_F_REQUEST | NLM_F_DUMP));
+ assertArrayEquals(INET_DIAG_REQ_V2_UDP_INET4_BYTES, msg);
+ }
+
+ // Hexadecimal representation of InetDiagReqV2 request.
+ private static final String INET_DIAG_REQ_V2_TCP_INET6_HEX =
+ // struct nlmsghdr
+ "48000000" + // length = 72
+ "1400" + // type = SOCK_DIAG_BY_FAMILY
+ "0100" + // flags = NLM_F_REQUEST
+ "00000000" + // seqno
+ "00000000" + // pid (0 == kernel)
+ // struct inet_diag_req_v2
+ "0a" + // family = AF_INET6
+ "06" + // protcol = IPPROTO_TCP
+ "00" + // idiag_ext
+ "00" + // pad
+ "ffffffff" + // idiag_states
+ // inet_diag_sockid
+ "a5de" + // idiag_sport = 42462
+ "b971" + // idiag_dport = 47473
+ "fe8000000000000086c9b2fffe6aed4b" + // idiag_src = fe80::86c9:b2ff:fe6a:ed4b
+ "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
+ "00000000" + // idiag_if
+ "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
+ private static final byte[] INET_DIAG_REQ_V2_TCP_INET6_BYTES =
+ HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET6_HEX.toCharArray(), false);
+
+ @Test
+ public void testInetDiagReqV2TcpInet6() throws Exception {
+ InetSocketAddress local = new InetSocketAddress(
+ InetAddress.getByName("fe80::86c9:b2ff:fe6a:ed4b"), 42462);
+ InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
+ 47473);
+ byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET6,
+ NLM_F_REQUEST);
+
+ assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
+ }
+
+ // Hexadecimal representation of InetDiagReqV2 request.
+ private static final String INET_DIAG_MSG_HEX =
+ // struct nlmsghdr
+ "58000000" + // length = 88
+ "1400" + // type = SOCK_DIAG_BY_FAMILY
+ "0200" + // flags = NLM_F_MULTI
+ "00000000" + // seqno
+ "f5220000" + // pid (0 == kernel)
+ // struct inet_diag_msg
+ "0a" + // family = AF_INET6
+ "01" + // idiag_state
+ "00" + // idiag_timer
+ "00" + // idiag_retrans
+ // inet_diag_sockid
+ "a817" + // idiag_sport = 43031
+ "960f" + // idiag_dport = 38415
+ "fe8000000000000086c9b2fffe6aed4b" + // idiag_src = fe80::86c9:b2ff:fe6a:ed4b
+ "00000000000000000000ffff08080808" + // idiag_dst = 8.8.8.8
+ "00000000" + // idiag_if
+ "ffffffffffffffff" + // idiag_cookie = INET_DIAG_NOCOOKIE
+ "00000000" + // idiag_expires
+ "00000000" + // idiag_rqueue
+ "00000000" + // idiag_wqueue
+ "a3270000" + // idiag_uid
+ "A57E1900"; // idiag_inode
+ private static final byte[] INET_DIAG_MSG_BYTES =
+ HexEncoding.decode(INET_DIAG_MSG_HEX.toCharArray(), false);
+
+ @Test
+ public void testParseInetDiagResponse() throws Exception {
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(INET_DIAG_MSG_BYTES);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
+ assertNotNull(msg);
+
+ assertTrue(msg instanceof InetDiagMessage);
+ final InetDiagMessage inetDiagMsg = (InetDiagMessage) msg;
+ assertEquals(10147, inetDiagMsg.mStructInetDiagMsg.idiag_uid);
+
+ final StructNlMsgHdr hdr = inetDiagMsg.getHeader();
+ assertNotNull(hdr);
+ assertEquals(NetlinkConstants.SOCK_DIAG_BY_FAMILY, hdr.nlmsg_type);
+ assertEquals(StructNlMsgHdr.NLM_F_MULTI, hdr.nlmsg_flags);
+ assertEquals(0, hdr.nlmsg_seq);
+ assertEquals(8949, hdr.nlmsg_pid);
+ }
+}
diff --git a/tests/net/java/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java
index d46facf..8604860 100644
--- a/tests/net/java/android/net/util/SharedLogTest.java
+++ b/tests/net/java/android/net/util/SharedLogTest.java
@@ -44,6 +44,8 @@
final SharedLog logLevel2a = logTop.forSubComponent("twoA");
final SharedLog logLevel2b = logTop.forSubComponent("twoB");
logLevel2b.e("2b or not 2b");
+ logLevel2b.e("No exception", null);
+ logLevel2b.e("Wait, here's one", new Exception("Test"));
logLevel2a.w("second post?");
final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
@@ -54,6 +56,9 @@
final String[] expected = {
" - MARK first post!",
" - [twoB] ERROR 2b or not 2b",
+ " - [twoB] ERROR No exception",
+ // No stacktrace in shared log, only in logcat
+ " - [twoB] ERROR Wait, here's one: Test",
" - [twoA] WARN second post?",
" - still logging",
" - [twoA.three] 3 >> 2",
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e3db7e8..1a05305 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -79,6 +79,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -248,7 +249,7 @@
@Spy private Resources mResources;
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
- MockContext(Context base) {
+ MockContext(Context base, ContentProvider settingsProvider) {
super(base);
mResources = spy(base.getResources());
@@ -260,7 +261,7 @@
});
mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
}
@Override
@@ -1048,7 +1049,9 @@
Looper.prepare();
}
- mServiceContext = new MockContext(InstrumentationRegistry.getContext());
+ FakeSettingsProvider.clearSettingsProvider();
+ mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
+ new FakeSettingsProvider());
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
@@ -1086,6 +1089,7 @@
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
+ FakeSettingsProvider.clearSettingsProvider();
}
private static int transportToLegacyType(int transport) {
@@ -4532,4 +4536,78 @@
mCellNetworkAgent.disconnect();
mCm.unregisterNetworkCallback(networkCallback);
}
+
+ @Test
+ public void testDataActivityTracking() throws RemoteException {
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ reset(mNetworkManagementService);
+ mCellNetworkAgent.connect(true);
+ networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_MOBILE));
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+
+ // Network switch
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.connect(true);
+ networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_WIFI));
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+
+ // Disconnect wifi and switch back to cell
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ assertNoCallbacks(networkCallback);
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_MOBILE));
+
+ // reconnect wifi
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ mWiFiNetworkAgent.connect(true);
+ networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+ // Disconnect cell
+ reset(mNetworkManagementService);
+ mCellNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
+ // sent as network being switched. Ensure rule removal for cell will not be triggered
+ // unexpectedly before network being removed.
+ waitForIdle();
+ verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+ verify(mNetworkManagementService, times(1)).removeNetwork(
+ eq(mCellNetworkAgent.getNetwork().netId));
+
+ // Disconnect wifi
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.disconnect();
+ waitFor(cv);
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+
+ // Clean up
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 40d5544..a6ed9f2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -33,6 +33,7 @@
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.WIFI_AP_STATE_ENABLED;
+import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -75,6 +76,8 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+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;
@@ -85,6 +88,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -146,6 +150,7 @@
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+ @Mock private DhcpServer mDhcpServer;
@Mock private INetd mNetd;
private final MockTetheringDependencies mTetheringDependencies =
@@ -240,6 +245,12 @@
public INetd getNetdService() {
return mNetd;
}
+
+ @Override
+ public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ DhcpServingParams params, SharedLog log) {
+ return mDhcpServer;
+ }
};
}
@@ -333,6 +344,7 @@
mServiceContext = new MockContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
mIntents = new Vector<>();
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -343,12 +355,16 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTetheringDependencies.reset();
- mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
- mLooper.getLooper(), mSystemProperties,
- mTetheringDependencies);
+ mTethering = makeTethering();
verify(mNMService).registerTetheringStatsProvider(any(), anyString());
}
+ private Tethering makeTethering() {
+ return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
+ mLooper.getLooper(), mSystemProperties,
+ mTetheringDependencies);
+ }
+
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
@@ -597,6 +613,18 @@
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+ verify(mDhcpServer, times(1)).start();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_IPv4LegacyDhcp() {
+ Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+ mTethering = makeTethering();
+ final NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ runUsbTethering(upstreamState);
+ sendIPv6TetherUpdates(upstreamState);
+
+ verify(mDhcpServer, never()).start();
}
@Test
@@ -620,6 +648,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
+ verify(mDhcpServer, times(1)).start();
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -633,6 +662,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mDhcpServer, times(1)).start();
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
TEST_XLAT_MOBILE_IFNAME);
@@ -649,6 +679,7 @@
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mDhcpServer, times(1)).start();
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
@@ -671,6 +702,8 @@
// Forwarding was not re-added for v6 (still times(1))
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ // DHCP not restarted on downstream (still times(1))
+ verify(mDhcpServer, times(1)).start();
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index bb31230..5217784 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -225,13 +225,4 @@
final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
assertFalse(cfg.enableLegacyDhcpServer);
}
-
- @Test
- public void testNewDhcpServerDefault() {
- Settings.Global.putString(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, null);
-
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
- // TODO: change to false when new server is promoted to default
- assertTrue(cfg.enableLegacyDhcpServer);
- }
}
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index f719552..b46a503 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -53,11 +53,11 @@
ConfigDescription();
ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
ConfigDescription(const ConfigDescription& o);
- ConfigDescription(ConfigDescription&& o);
+ ConfigDescription(ConfigDescription&& o) noexcept;
ConfigDescription& operator=(const android::ResTable_config& o);
ConfigDescription& operator=(const ConfigDescription& o);
- ConfigDescription& operator=(ConfigDescription&& o);
+ ConfigDescription& operator=(ConfigDescription&& o) noexcept;
ConfigDescription CopyWithoutSdkVersion() const;
@@ -124,7 +124,7 @@
*static_cast<android::ResTable_config*>(this) = o;
}
-inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
+inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept {
*this = o;
}
@@ -141,7 +141,7 @@
return *this;
}
-inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
+inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) noexcept {
*this = o;
return *this;
}
diff --git a/tools/aapt2/development.md b/tools/aapt2/development.md
new file mode 100644
index 0000000..8ee873a
--- /dev/null
+++ b/tools/aapt2/development.md
@@ -0,0 +1,11 @@
+# AAPT2 development
+
+## Building
+All build targets can be found in `Android.bp` file. The main ones are `make -j aapt2` and `make -j aapt2_tests`
+
+`make -j aapt2` will create an aapt2 executable in `out/host/linux-x86/bin/aapt2` (on Linux). This `aapt2` executable will then be used for all the apps in the platform.
+
+Static version of the tool (without shared libraries) can be built with `make -j static_sdk_tools dist DIST_DIR=$OUTPUT_DIRECTORY BUILD_HOST_static=1`. Note, in addition to aapt2 this command will also output other statically built tools to the `$OUTPUT_DIRECTORY`.
+
+## Running tests
+Build `make -j aapt2_tests` and then (on Linux) execute `out/host/linux-x86/nativetest64/aapt2_tests/aapt2_tests`
\ No newline at end of file
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index c0eaa8e..7872738 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -41,46 +41,46 @@
ASSERT_FALSE(in.HadError());
EXPECT_THAT(in.ByteCount(), Eq(0u));
- const char* buffer;
+ const void* buffer;
size_t size;
- ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)) << in.GetError();
+ ASSERT_TRUE(in.Next(&buffer, &size)) << in.GetError();
ASSERT_THAT(size, Eq(10u));
ASSERT_THAT(buffer, NotNull());
EXPECT_THAT(in.ByteCount(), Eq(10u));
- EXPECT_THAT(StringPiece(buffer, size), Eq("this is a "));
+ EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("this is a "));
- ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_TRUE(in.Next(&buffer, &size));
ASSERT_THAT(size, Eq(10u));
ASSERT_THAT(buffer, NotNull());
EXPECT_THAT(in.ByteCount(), Eq(20u));
- EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+ EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin"));
in.BackUp(5u);
EXPECT_THAT(in.ByteCount(), Eq(15u));
- ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_TRUE(in.Next(&buffer, &size));
ASSERT_THAT(size, Eq(5u));
ASSERT_THAT(buffer, NotNull());
ASSERT_THAT(in.ByteCount(), Eq(20u));
- EXPECT_THAT(StringPiece(buffer, size), Eq("strin"));
+ EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("strin"));
// Backup 1 more than possible. Should clamp.
in.BackUp(11u);
EXPECT_THAT(in.ByteCount(), Eq(10u));
- ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_TRUE(in.Next(&buffer, &size));
ASSERT_THAT(size, Eq(10u));
ASSERT_THAT(buffer, NotNull());
ASSERT_THAT(in.ByteCount(), Eq(20u));
- EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+ EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin"));
- ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_TRUE(in.Next(&buffer, &size));
ASSERT_THAT(size, Eq(1u));
ASSERT_THAT(buffer, NotNull());
ASSERT_THAT(in.ByteCount(), Eq(21u));
- EXPECT_THAT(StringPiece(buffer, size), Eq("g"));
+ EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("g"));
- EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ EXPECT_FALSE(in.Next(&buffer, &size));
EXPECT_FALSE(in.HadError());
}
@@ -93,25 +93,25 @@
ASSERT_FALSE(out.HadError());
EXPECT_THAT(out.ByteCount(), Eq(0u));
- char* buffer;
+ void* buffer;
size_t size;
- ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+ ASSERT_TRUE(out.Next(&buffer, &size));
ASSERT_THAT(size, Eq(10u));
ASSERT_THAT(buffer, NotNull());
EXPECT_THAT(out.ByteCount(), Eq(10u));
- memcpy(buffer, input.c_str(), size);
+ memcpy(reinterpret_cast<char*>(buffer), input.c_str(), size);
- ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+ ASSERT_TRUE(out.Next(&buffer, &size));
ASSERT_THAT(size, Eq(10u));
ASSERT_THAT(buffer, NotNull());
EXPECT_THAT(out.ByteCount(), Eq(20u));
- memcpy(buffer, input.c_str() + 10u, size);
+ memcpy(reinterpret_cast<char*>(buffer), input.c_str() + 10u, size);
- ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+ ASSERT_TRUE(out.Next(&buffer, &size));
ASSERT_THAT(size, Eq(10u));
ASSERT_THAT(buffer, NotNull());
EXPECT_THAT(out.ByteCount(), Eq(30u));
- buffer[0] = input[20u];
+ reinterpret_cast<char*>(buffer)[0] = input[20u];
out.BackUp(size - 1);
EXPECT_THAT(out.ByteCount(), Eq(21u));
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index be67c9c..10e504e 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -26,21 +26,20 @@
#include "util/Maybe.h"
#include "xml/XmlDom.h"
-using ::android::StringPiece;
using ::aapt::text::IsJavaIdentifier;
namespace aapt {
-static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
+static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
const std::string& value) {
- StringPiece result = value;
+ std::string result = value;
size_t pos = value.rfind('.');
if (pos != std::string::npos) {
result = result.substr(pos + 1);
}
// Normalize only the java identifier, leave the original value unchanged.
- if (result.contains("-")) {
+ if (result.find("-") != std::string::npos) {
result = JavaClassGenerator::TransformToFieldName(result);
}
@@ -64,7 +63,7 @@
return false;
}
- Maybe<StringPiece> result =
+ Maybe<std::string> result =
ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
if (!result) {
return false;
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index 3045255..d4b3abc 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -68,7 +68,7 @@
*/
explicit BigBuffer(size_t block_size);
- BigBuffer(BigBuffer&& rhs);
+ BigBuffer(BigBuffer&& rhs) noexcept;
/**
* Number of occupied bytes in all the allocated blocks.
@@ -136,7 +136,7 @@
inline BigBuffer::BigBuffer(size_t block_size)
: block_size_(block_size), size_(0) {}
-inline BigBuffer::BigBuffer(BigBuffer&& rhs)
+inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept
: block_size_(rhs.block_size_),
size_(rhs.size_),
blocks_(std::move(rhs.blocks_)) {}
diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h
index 59858e4..1727b18 100644
--- a/tools/aapt2/util/ImmutableMap.h
+++ b/tools/aapt2/util/ImmutableMap.h
@@ -32,8 +32,8 @@
using const_iterator =
typename std::vector<std::pair<TKey, TValue>>::const_iterator;
- ImmutableMap(ImmutableMap&&) = default;
- ImmutableMap& operator=(ImmutableMap&&) = default;
+ ImmutableMap(ImmutableMap&&) noexcept = default;
+ ImmutableMap& operator=(ImmutableMap&&) noexcept = default;
static ImmutableMap<TKey, TValue> CreatePreSorted(
std::initializer_list<std::pair<TKey, TValue>> list) {
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 9a82418..031276c 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -46,7 +46,7 @@
template <typename U>
Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
- Maybe(Maybe&& rhs);
+ Maybe(Maybe&& rhs) noexcept;
template <typename U>
Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
@@ -56,7 +56,7 @@
template <typename U>
Maybe& operator=(const Maybe<U>& rhs);
- Maybe& operator=(Maybe&& rhs);
+ Maybe& operator=(Maybe&& rhs) noexcept;
template <typename U>
Maybe& operator=(Maybe<U>&& rhs);
@@ -134,7 +134,7 @@
}
template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) {
+Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) {
if (!rhs.nothing_) {
rhs.nothing_ = true;
@@ -192,7 +192,7 @@
}
template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
+inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept {
// Delegate to the actual assignment.
return move(std::forward<Maybe<T>>(rhs));
}
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 934847f..91cd1cb 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -492,6 +492,7 @@
def verify_protected(clazz):
"""Verify that no protected methods or fields are allowed."""
for m in clazz.methods:
+ if m.name == "finalize": continue
if "protected" in m.split:
error(clazz, m, "M7", "Protected methods not allowed; must be public")
for f in clazz.fields:
@@ -1025,6 +1026,10 @@
# Resources defined by files are foo_bar_baz
if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]:
for f in clazz.fields:
+ if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue
+ if f.name.startswith("config_"):
+ error(clazz, f, None, "Expected config name to be config_fooBarBaz style")
+
if re.match("[a-z1-9_]+$", f.name): continue
error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style")
@@ -1361,6 +1366,60 @@
error(clazz, m, None, "Provide an explicit copy constructor instead of implementing clone()")
+def verify_pfd(clazz):
+ """Verify that android APIs use PFD over FD."""
+ examine = clazz.ctors + clazz.methods
+ for m in examine:
+ if m.typ == "java.io.FileDescriptor":
+ error(clazz, m, "FW11", "Must use ParcelFileDescriptor")
+ if m.typ == "int":
+ if "Fd" in m.name or "FD" in m.name or "FileDescriptor" in m.name:
+ error(clazz, m, "FW11", "Must use ParcelFileDescriptor")
+ for arg in m.args:
+ if arg == "java.io.FileDescriptor":
+ error(clazz, m, "FW11", "Must use ParcelFileDescriptor")
+
+ for f in clazz.fields:
+ if f.typ == "java.io.FileDescriptor":
+ error(clazz, f, "FW11", "Must use ParcelFileDescriptor")
+
+
+def verify_numbers(clazz):
+ """Discourage small numbers types like short and byte."""
+
+ discouraged = ["short","byte"]
+
+ for c in clazz.ctors:
+ for arg in c.args:
+ if arg in discouraged:
+ warn(clazz, c, "FW12", "Should avoid odd sized primitives; use int instead")
+
+ for f in clazz.fields:
+ if f.typ in discouraged:
+ warn(clazz, f, "FW12", "Should avoid odd sized primitives; use int instead")
+
+ for m in clazz.methods:
+ if m.typ in discouraged:
+ warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead")
+ for arg in m.args:
+ if arg in discouraged:
+ warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead")
+
+
+def verify_singleton(clazz):
+ """Catch singleton objects with constructors."""
+
+ singleton = False
+ for m in clazz.methods:
+ if m.name.startswith("get") and m.name.endswith("Instance") and " static " in m.raw:
+ singleton = True
+
+ if singleton:
+ for c in clazz.ctors:
+ error(clazz, c, None, "Singleton classes should use getInstance() methods")
+
+
+
def is_interesting(clazz):
"""Test if given class is interesting from an Android PoV."""
@@ -1431,6 +1490,9 @@
verify_tense(clazz)
verify_icu(clazz)
verify_clone(clazz)
+ verify_pfd(clazz)
+ verify_numbers(clazz)
+ verify_singleton(clazz)
def examine_stream(stream):
diff --git a/tools/fonts/add_additional_fonts.py b/tools/fonts/add_additional_fonts.py
deleted file mode 100644
index bf4af2b..0000000
--- a/tools/fonts/add_additional_fonts.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import sys
-
-def main(argv):
- original_file = 'frameworks/base/data/fonts/fonts.xml'
-
- if len(argv) == 3:
- output_file_path = argv[1]
- override_file_path = argv[2]
- else:
- raise ValueError("Wrong number of arguments %s" % len(argv))
-
- fallbackPlaceholderFound = False
- with open(original_file, 'r') as input_file:
- with open(output_file_path, 'w') as output_file:
- for line in input_file:
- # If we've found the spot to add additional fonts, add them.
- if line.strip() == '<!-- fallback fonts -->':
- fallbackPlaceholderFound = True
- with open(override_file_path) as override_file:
- for override_line in override_file:
- output_file.write(override_line)
- output_file.write(line)
- if not fallbackPlaceholderFound:
- raise ValueError('<!-- fallback fonts --> not found in source file: %s' % original_file)
-
-if __name__ == '__main__':
- main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 6c46e67..fdc800b 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -212,8 +212,8 @@
move_from_files(args.input_greylists, uncategorized, light_greylist)
move_from_files(args.input_blacklists, uncategorized, blacklist)
- # Iterate over all uncategorized members and move serialization API to light greylist.
- move_serialization(uncategorized, light_greylist)
+ # Iterate over all uncategorized members and move serialization API to whitelist.
+ move_serialization(uncategorized, whitelist)
# Extract package names of members from whitelist and light greylist, which
# are assumed to have been finalized at this point. Assign all uncategorized
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 8f79318..4716241 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -85,5 +85,23 @@
self.assertEqual(
dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ]))
+ 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)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 639f980..0cf1046 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -138,7 +138,7 @@
size_t base = 0;
size_t found;
while (true) {
- found = args.find_first_of(" ", base);
+ found = args.find_first_of(' ', base);
if (found != base) {
string arg = args.substr(base, found - base);
printf(" \"%s\",", arg.c_str());
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 9915479..56c8428 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -234,9 +234,11 @@
}
}
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", const std::map<int, int64_t>& arg%d_1, "
- "const std::map<int, char const*>& arg%d_2, "
- "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex);
+ fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
+ "const std::map<int, int64_t>& arg%d_2, "
+ "const std::map<int, char const*>& arg%d_3, "
+ "const std::map<int, float>& arg%d_4",
+ argIndex, argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -302,6 +304,13 @@
fprintf(out, " event.end();\n");
fprintf(out, " }\n");
+ fprintf(out, " for (const auto& it : arg%d_4) {\n", argIndex);
+ fprintf(out, " event.begin();\n");
+ fprintf(out, " event << it.first;\n");
+ fprintf(out, " event << it.second;\n");
+ fprintf(out, " event.end();\n");
+ fprintf(out, " }\n");
+
fprintf(out, " event.end();\n\n");
} else {
if (*arg == JAVA_TYPE_STRING) {
@@ -344,9 +353,11 @@
}
}
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", const std::map<int, int64_t>& arg%d_1, "
- "const std::map<int, char const*>& arg%d_2, "
- "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex);
+ fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
+ "const std::map<int, int64_t>& arg%d_2, "
+ "const std::map<int, char const*>& arg%d_3, "
+ "const std::map<int, float>& arg%d_4",
+ argIndex, argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -374,7 +385,8 @@
}
}
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", arg%d_1, arg%d_2, arg%d_3", argIndex, argIndex, argIndex);
+ fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4",
+ argIndex, argIndex, argIndex, argIndex);
} else {
fprintf(out, ", arg%d", argIndex);
}
@@ -529,10 +541,14 @@
}
}
} else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", const std::map<int, int64_t>& %s_int"
+ fprintf(out, ", const std::map<int, int32_t>& %s_int"
+ ", const std::map<int, int64_t>& %s_long"
", const std::map<int, char const*>& %s_str"
", const std::map<int, float>& %s_float",
- field->name.c_str(), field->name.c_str(), field->name.c_str());
+ field->name.c_str(),
+ field->name.c_str(),
+ field->name.c_str(),
+ field->name.c_str());
} else {
fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
}
@@ -561,9 +577,11 @@
}
}
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", const std::map<int, int64_t>& arg%d_1, "
- "const std::map<int, char const*>& arg%d_2, "
- "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex);
+ fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
+ "const std::map<int, int64_t>& arg%d_2, "
+ "const std::map<int, char const*>& arg%d_3, "
+ "const std::map<int, float>& arg%d_4",
+ argIndex, argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -976,6 +994,7 @@
}
static void write_key_value_map_jni(FILE* out) {
+ fprintf(out, " std::map<int, int32_t> int32_t_map;\n");
fprintf(out, " std::map<int, int64_t> int64_t_map;\n");
fprintf(out, " std::map<int, float> float_map;\n");
fprintf(out, " std::map<int, char const*> string_map;\n\n");
@@ -989,9 +1008,11 @@
fprintf(out, " std::vector<std::unique_ptr<ScopedUtfChars>> scoped_ufs;\n\n");
+ fprintf(out, " jclass jint_class = env->FindClass(\"java/lang/Integer\");\n");
fprintf(out, " jclass jlong_class = env->FindClass(\"java/lang/Long\");\n");
fprintf(out, " jclass jfloat_class = env->FindClass(\"java/lang/Float\");\n");
fprintf(out, " jclass jstring_class = env->FindClass(\"java/lang/String\");\n");
+ fprintf(out, " jmethodID jget_int_method = env->GetMethodID(jint_class, \"intValue\", \"()I\");\n");
fprintf(out, " jmethodID jget_long_method = env->GetMethodID(jlong_class, \"longValue\", \"()J\");\n");
fprintf(out, " jmethodID jget_float_method = env->GetMethodID(jfloat_class, \"floatValue\", \"()F\");\n\n");
@@ -1000,7 +1021,9 @@
fprintf(out, " jint key = env->CallIntMethod(value_map, jget_key_method, i);\n");
fprintf(out, " jobject jvalue_obj = env->CallObjectMethod(value_map, jget_value_method, i);\n");
fprintf(out, " if (jvalue_obj == NULL) { continue; }\n");
- fprintf(out, " if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n");
+ fprintf(out, " if (env->IsInstanceOf(jvalue_obj, jint_class)) {\n");
+ fprintf(out, " int32_t_map[key] = env->CallIntMethod(jvalue_obj, jget_int_method);\n");
+ fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n");
fprintf(out, " int64_t_map[key] = env->CallLongMethod(jvalue_obj, jget_long_method);\n");
fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jfloat_class)) {\n");
fprintf(out, " float_map[key] = env->CallFloatMethod(jvalue_obj, jget_float_method);\n");
@@ -1129,7 +1152,7 @@
}
}
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", int64_t_map, string_map, float_map");
+ fprintf(out, ", int32_t_map, int64_t_map, string_map, float_map");
} else {
const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ce8d71d..58c1300 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -332,9 +332,10 @@
public String preSharedKey;
/**
- * Up to four WEP keys. Either an ASCII string enclosed in double
- * quotation marks (e.g., {@code "abcdef"}) or a string
- * of hex digits (e.g., {@code 0102030405}).
+ * Four WEP keys. For each of the four values, provide either an ASCII
+ * string enclosed in double quotation marks (e.g., {@code "abcdef"}),
+ * a string of hex digits (e.g., {@code 0102030405}), or an empty string
+ * (e.g., {@code ""}).
* <p/>
* When the value of one of these keys is read, the actual key is
* not returned, just a "*" if the key has a value, or the null
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7a91347..59ba8e7 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1698,9 +1698,7 @@
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get valid results. If there is a remote exception (e.g., either a communication
- * problem with the system service or an exception within the framework) an empty list will be
- * returned.
+ * in order to get valid results.
*/
public List<ScanResult> getScanResults() {
try {